zzm99


  • Home

  • Tags

  • Categories

  • Archives

  • Search

char[],char *,string之间转换

Posted on 2019-07-03 | In C++ , 其他 |
Words count in article: 743 | Reading time ≈ 3

char []与char *之间转换

char []转char *:直接进行赋值即可

1
2
3
4
// char[] 转char *
char str[] = "lala";
char *str1 = str;
cout << str1 << endl;

char *转char[]:字符拷贝实现,不能进行赋值操作

1
2
3
4
5
// char *转换为char []
const char *st = "hehe";
char st1[] = "lalalala";
strncpy(st1, st, strlen(st) + 1); // 注意加1操作
cout << st1 << endl;

char 与const char 之间转换

const char 转char :拷贝实现,不能进行赋值

1
2
3
4
5
6
7
8
9
// const char *转char *
const char *st = "lala";
// 直接赋值不可以
//char *st1 = st; // (不可以编译器报错)
//cout << st1 << endl;
// 另外开辟空间,将字符一个一个复制过去
char *ncstr = new char[strlen(st) + 1];
strcpy(ncstr, st);
cout << ncstr << endl;

char 转const char :直接进行赋值

1
2
3
4
// char *转const char * 
char *st = "hehe"; // (编译提示警告)
const char *st1 = st;
cout << st1 << endl;

char *与string之间转换

char *转string:1)直接赋值;2)构造转换实现

1
2
3
4
5
6
7
8
9
10
11
12
// char*转换为string
// (注意,定义char *变量,并直接赋值,最好定义为const变量,否则编译器警告)
const char *st = "hello";
// 赋值转换
string st1 = st;
cout << st1 << endl;
// 构造转换
string s1(st, st + strlen(st));
cout << s1 << endl;
// 改变const char *变量值
st = "lalala";
cout << st << endl;

string转char *:赋值操作(注意类型转换)

1
2
3
4
5
6
// string转char *
string st = "My test";
//char *st1 = st; // 错误类型不同
//char *st1 = st.c_str(); // 错误类型不同
char *st1 = const_cast<char *>(st.c_str()) ; //c_str得到的是const char *
cout << st1 << endl;

char[]与string之间转换

char []转string:1)直接赋值;2)构造转换实现

1
2
3
4
5
6
7
8
// char[]转换为string 
char st[] = "hello";
// 直接赋值实现
string st1 = st;
cout << st1 << endl;
// 构造实现
string st2(st, st + strlen(st));
cout << st2 << endl;

string转char[]:拷贝实现,不能直接赋值

1
2
3
4
5
6
7
8
// string转char []
string ts = "My test1";
//char ts1[] = ts; // 错误
//char ts1[] = const_cast<char *>(ts.c_str()); // 错误
char ts1[] = "lalallalalaaaa";
strncpy(ts1, ts.c_str(), ts.length() + 1); // 注意,一定要加1,否则没有赋值'\0'
cout << ts1 << endl;
return 0;

比较通用的类型转换,利用sstream库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//test
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main()
{
stringstream turnturn;
const char *sss = "ssssssss";
int s = 1111;
turnturn << s;
string rem;
turnturn >> rem;
cout << rem;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//test
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

int main(int argc, char *argv[])
{
stringstream stream;
// 将string转换为int
string text = "123456";
stream << text;
int n = 0;
stream >> n;
n++;
cout << n << endl;

text = "";
// 注意这句clear必不可少,注释掉之后stream的<<操作无效
stream.clear();
stream.str("");
// 将int转换为string
stream << n;
stream >> text;
// 加一句hello方便区分
text += "hello";
cout << text << endl;
system("pause");
return 0;
}
  • 如果你想要转换多个int/double等类型的变量,而又始终用一个stringstream对象(避免总是创建stringstream的开销),那么你就需要在再一次用stringstream之前将stringstream的内容清空。
1
2
stream.clear();	//重置状态
stream.str(""); //清空数据

c++函数不定长参数

Posted on 2019-06-30 | In C++ , 其他 |
Words count in article: 523 | Reading time ≈ 2

知道数据的具体类型时:

比如已经知道数据是int型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
using namespace std;
int sum(int a, ...)
{
int *temp = &a;
int sum = 0;
temp++;
temp++;
for(int i=0; i<a; i++)
{
sum += *temp;
cout << *temp << endl;
temp++;
temp++;
}
return sum;
}

int main(){
cout << sum(4,1,2,3,4) << endl;
return 0;
}

测试结果显示指针要前进两位才正确。。。

C 标准库 - <stdarg.h>

简介

stdarg.h 头文件定义了一个变量类型 va_list 和三个宏,这三个宏可用于在参数个数未知(即参数个数可变)时获取函数中的参数。

可变参数的函数通在参数列表的末尾是使用省略号(,…)定义的。

库变量

下面是头文件 stdarg.h 中定义的变量类型:

va_list 这是一个适用于 va_start()、va_arg() 和 va_end() 这三个宏存储信息的类型。

库宏

下面是头文件 stdarg.h 中定义的宏:

void va_start(va_list ap, last_arg)

这个宏初始化 ap 变量,它与 va_arg 和 va_end 宏是一起使用的。last_arg 是最后一个传递给函数的已知的固定参数,即省略号之前的参数。

type va_arg(va_list ap, type)

这个宏检索函数参数列表中类型为 type 的下一个参数。

void va_end(va_list ap)

这个宏允许使用了 va_start 宏的带有可变参数的函数返回。如果在从函数返回之前没有调用 va_end,则结果为未定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#include <string.h>
#include <stdarg.h>

void tLog(char *sInfo, ...);

int main(int argc, char *argv[])
{
char str[4+1] = {0};
strcpy(str,"INFO");
tLog(str,argv[1],argv[2],argv[3]);
return 0;
}
void tLog(char *sInfo, ...)
{
char *str = NULL;
char *str1 = NULL;
char *str2 = NULL;
va_list sArgv; // 申请参数列表变量
va_start(sArgv, sInfo); // 申明最后一个传递给函数的已知的固定参数
/* 依次读取固定参数 sInfo 后的 参数 不包括sInfo*/
str = va_arg(sArgv, char*);
str1 = va_arg(sArgv, char*);
str2 = va_arg(sArgv, char*);
va_end(sArgv);
printf("运行输出:[%s,%s%s%s]\n",sInfo,str,str1,str2);
}
1
2
$ ./a.out  Runoob Google Facebook
运行输出:[INFO,RunoobGoogleFacebook]

C++四种类型转换运算符的使用方法

Posted on 2019-06-27 | In C++ , 其他 |
Words count in article: 1.4k | Reading time ≈ 6

1、reinterpret_cast

该函数将一个类型的指针转换为另一个类型的指针.

这种转换不用修改指针变量值存放格式(不改变指针变量值),只需在编译时重新解释指针的类型就可做到.
reinterpret_cast 可以将指针值转换为一个整型数,但不能用于非指针类型的转换.
例:

1
2
3
4
5
6
7
8
9
10
11
12
13
//基本类型指针的类型转换
double d=9.2;
double* pd = &d;
int *pi = reinterpret_cast<int*>(pd);  //相当于int *pi = (int*)pd;

//不相关的类的指针的类型转换
class A{};
class B{};
A* pa = new A;
B* pb = reinterpret_cast<B*>(pa);   //相当于B* pb = (B*)pa;

//指针转换为整数
long l = reinterpret_cast<long>(pi);   //相当于long l = (long)pi;

2、const_cast

该函数用于去除指针变量的常量属性,将它转换为一个对应指针类型的普通变量。反过来,也可以将一个非常量的指针变量转换为一个常指针变量。
这种转换是在编译期间做出的类型更改。
例:

1
2
3
4
5
6
const int* pci = 0;
int* pk = const_cast<int*>(pci);  //相当于int* pk = (int*)pci;


const A* pca = new A;
A* pa = const_cast<A*>(pca);     //相当于A* pa = (A*)pca;

出于安全性考虑,const_cast无法将非指针的常量转换为普通变量。

3、static_cast

该函数主要用于基本类型之间和具有继承关系的类型之间的转换。
这种转换一般会更改变量的内部表示方式,因此,static_cast应用于指针类型转换没有太大意义。

主要有如下几种用法:

  • 用于类层次结构中基类和子类之间指针或引用的转换。

  • 进行上行转换(把子类的指针或引用转换成基类表示)是安全的;

  • 进行下行转换(把基类指针或引用转换成子类指针或引用)时,由于没有动态类型检查,所以是不安全的。

  • 用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。

  • 把void指针转换成目标类型的指针(不安全!!)

  • 把任何类型的表达式转换成void类型。

例:

1
2
3
4
5
6
7
8
9
//基本类型转换
int i=0;
double d = static_cast<double>(i);  //相当于 double d = (double)i;

//转换继承类的对象为基类对象
class Base{};
class Derived : public Base{};
Derived d;
Base b = static_cast<Base>(d);     //相当于 Base b = (Base)d;

4、dynamic_cast

用法:dynamic_cast < type-id > ( expression )
说明:该运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void *;
如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。
它与static_cast相对,是动态转换。
这种转换是在运行时进行转换分析的,并非在编译时进行,明显区别于上面三个类型转换操作。
该函数只能在继承类对象的指针之间或引用之间进行类型转换。进行转换时,会根据当前运行时类型信息,判断类型对象之间的转换是否合法。dynamic_cast的指针转换失败,可通过是否为null检测,引用转换失败则抛出一个bad_cast异常。
例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Base{};
class Derived : public Base{};


//派生类指针转换为基类指针
Derived *pd = new Derived;
Base *pb = dynamic_cast<Base*>(pd);

if (!pb)
    cout << "类型转换失败" << endl;

//没有继承关系,但被转换类有虚函数
class A
{

virtual ~A(){};

}   //有虚函数
class B{}:
A* pa = new A;
B* pb  = dynamic_cast<B*>(pa);

如果对无继承关系或者没有虚函数的对象指针进行转换、基本类型指针转换以及基类指针转换为派生类指针,都不能通过编译。

应用:

解决因引用或指针使用了父类而实质为子类的对象访问不到自己的private变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include <cmath> 

class Point2D
{
private:
double x;
double y;

public:
Point2D() {
x = 0;
y = 0;
}

double getX() {
return x;
}

double getY() {
return y;
}

void setX(double x) {
this->x = x;
}

void setY(double y) {
this->y = y;
}

virtual double distance(Point2D & point2) {
double dx = x - point2.x;
double dy = y - point2.y;
return sqrt(dx * dx + dy * dy);
}

};

class Point3D : public Point2D
{
private:
double z;
public:
Point3D():z(0), Point2D(){}
Point3D(double a, double b, double c):z(c){
setX(a);
setY(b);
}
double getZ() {
return this->z;
}
virtual double distance(Point2D & point2)
{
Point3D* rem = static_cast<Point3D*>(&point2);
double dz = getZ() - rem->z;
double dx = getX() - point2.getX();
double dy = getY() - point2.getY();
return sqrt(dz*dz + dx * dx + dy * dy);
}
};

#include <iostream>
using namespace std;

void printDistance(Point2D & point1, Point2D & point2) {
cout << "point1.distance(point2) = " <<
point1.distance(point2) << endl;
}

int main() {
double x1;
double y1;
double z1;
double x2;
double y2;
double z2;
cin >> x1;
cin >> y1;
cin >> z1;
cin >> x2;
cin >> y2;
cin >> z2;

Point3D point1(x1, y1, z1);
Point3D point2(x2, y2, z2);

cout << "point1.getX() = " << point1.getX() << endl;
cout << "point1.getY() = " << point1.getY() << endl;
cout << "point1.getZ() = " << point1.getZ() << endl;
cout << "point2.getX() = " << point2.getX() << endl;
cout << "point2.getY() = " << point2.getY() << endl;
cout << "point2.getZ() = " << point2.getZ() << endl;

printDistance(point1, point2);
}
1
2
3
4
5
6
7
8
9
10
11
EXAMPLE INPUT:
1 2 3
10 20 30
EXAMPLE OUTPUT:
point1.getX() = 1
point1.getY() = 2
point1.getZ() = 3
point2.getX() = 10
point2.getY() = 20
point2.getZ() = 30
point1.distance(point2) = 33.6749

关于虚函数virtual

Posted on 2019-06-25 | In C++ , 其他 |
Words count in article: 627 | Reading time ≈ 2

只有虚函数才能使你在仅用一个指针进行调用时,确保访问到期望的方法。非虚函数就没有这个特性。

见代码效果: 区别只在于有没有virtual

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream> 
using namespace std;

class Bus
{
int number;
protected:
int getNumber() const {
return number;
}
void setNumber(int number)
{
this->number = number;
}
public:
Bus(){}
Bus(int number) {
this->number = number;
}
virtual void print() const {
cout << "No. " << number << " bus." << endl;
}
};

class YellowBus : public Bus
{
public:
YellowBus(int number) {
setNumber(number);
}
void print() const {
cout << "No." << getNumber() << " yellow bus." << endl;
}
};

void print1(const Bus & bus) {
bus.print();
}

void print2(const Bus bus) {
bus.print();
}

int main() {
int number;
cin >> number;
YellowBus yellow(number);
print1(yellow);
print2(yellow);
}
1
2
3
4
100

No. 100 yellow bus.
No. 100 bus.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream> 
using namespace std;

class Bus
{
int number;
protected:
int getNumber() const {
return number;
}
void setNumber(int number)
{
this->number = number;
}
public:
Bus(){}
Bus(int number) {
this->number = number;
}
void print() const {
cout << "No. " << number << " bus." << endl;
}
};

class YellowBus : public Bus
{
public:
YellowBus(int number) {
setNumber(number);
}
void print() const {
cout << "No." << getNumber() << " yellow bus." << endl;
}
};

void print1(const Bus & bus) {
bus.print();
}

void print2(const Bus bus) {
bus.print();
}

int main() {
int number;
cin >> number;
YellowBus yellow(number);
print1(yellow);
print2(yellow);
}
1
2
3
4
100

No. 100 bus.
No. 100 bus.
  1. 那么virtual函数应该在什么情况下用呢?
  • virtual 函数应该在某个类的某个函数需要多态性的情况下使用
  • 还有一个使用场景是若类存在派生,那么析构函数必须是virtual的,否则可能会导致资源泄露。
  • 而非 virtual 函数,能够确保你使用基类指针就一定调用基类实现,防止派生类无意中覆盖。
  1. 是不是用virtual的函数都能用非virtual的函数替代?
  • 正相反,若在开发过程中做了良好约定,所有的非virtual函数都能用virtual函数代替。
  • 由于调用virtual函数多了一次查vtable的动作,可能导致(微不可查)的性能损失。不过这并非是不可接受的,某些框架例如Qt,大部分类都是采用这种哲学进行设计,并没有什么问题。
  • 有些语言比如 python 就是这么干的,程序员自己去保障覆盖问题。
  1. 那么是不是virtual没什么用呢?
  • 若是不考虑效率和强行避免覆写问题,其实非virtual函数才是没什么用的。

linux编译c++

Posted on 2019-06-17 | In 其他 |
Words count in article: 55 | Reading time ≈ 1

linux编译并运行c++

如果是编译运行c语言,则是gcc

1
2
$ g++ helloworld.cpp
$ ./a.out

当前目录产生一个a.out可执行文件


指定可执行文件名:

1
2
$ g++ helloworld.cpp -o helloworld
$ ./helloworld

C++ 输入输出

Posted on 2019-06-17 | In C++ , 其他 |
Words count in article: 4k | Reading time ≈ 14

关于c++格式化输入输出相关的,简单的其实可以直接 #include <cstdio>

C++ 输入输出流

输入和输出并不是C++语言中的正式组成成分。C和C++本身都没有为输入和输出提供专门的语句结构。输入输出不是由C++本身定义的,而是在编译系统提供的I/O库中定义的。

C++ 的程序把输入和输出看做字节流。输入时,程序从输入流中抽取字节;输出时,程序将字节插入输出流中,流充当了程序和流源或流目标之间的桥梁。

C++输入输出中有两个基类,分别为ios_base和ios,而基于这两个基类实现了输入流istream和输出流ostream,然后基于这两个流实现了针对三种不同应用的类(或对象):

  • 标准输入输出:为了程序员使用方便,C++直接以对象的形式提供了几个标准输入输出工具,分别为:cin(标准输入),cout(标准输出),cerr(不带缓存的标准错误输出),clog(带缓存的标准错误输出)。
  • 文件输入输出:C++ 提供了三个类以方便程序员进行文件读写操作,分别为:ifstream(读文件),ofstream(写文件),fstream(读写文件)
  • 字符串输入输出流:将字符串作为输入输出流进行处理,有三个对应的类,分别为:istringstream(字符串输入流),ostringstream(字符串输出流),stringstream(字符串输入输出流)。
    Read more »

C语言标准输入输出、相关函数

Posted on 2019-06-17 | In C++ , 其他 |
Words count in article: 3.4k | Reading time ≈ 14

C语言标准输入输出、相关函数

格式化输入输出函数:

printf()函数:

格式字符串

在Turbo C中格式字符串的一般形式为:
[标志][输出最小宽度][.精度][长度]类型。
其中方括号[]中的项为可选项。

1) 类型
类型字符用以表示输出数据的类型,其格式符和意义如下表所示:

1

2) 标志
标志字符为 -、+、# 和空格四种,其意义下表所示:

1

3) 输出最小宽度
用十进制整数来表示输出的最少位数。若实际位数多于定义的宽度,则按实际位数输出,若实际位数少于定义的宽度则补以空格或0。

Read more »

Java 多线程编程

Posted on 2019-06-11 | In java , 基础知识 |
Words count in article: 2.2k | Reading time ≈ 9

Java 多线程编程

Java 给多线程编程提供了内置的支持。 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销。

这里定义和线程相关的另一个术语 - 进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。

多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。

一个线程的生命周期

线程是一个动态执行的过程,它也有一个从产生到死亡的过程。

下图显示了一个线程完整的生命周期。

1

  • 新建状态:
    使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。

  • 就绪状态:
    当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。

  • 运行状态:
    如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

  • 阻塞状态:
    如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:

等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。

同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。

其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。

  • 死亡状态:
    一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

线程的优先级

每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。

Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。

默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)。

具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。

创建一个线程

Java 提供了三种创建线程的方法:

  • 通过实现 Runnable 接口;
  • 通过继承 Thread 类本身;
  • 通过 Callable 和 Future 创建线程。

通过实现 Runnable 接口来创建线程

创建一个线程,最简单的方法是创建一个实现 Runnable 接口的类。

为了实现 Runnable,一个类只需要执行一个方法调用 run(),声明如下:

public void run()
你可以重写该方法,重要的是理解的 run() 可以调用其他方法,使用其他类,并声明变量,就像主线程一样。

在创建一个实现 Runnable 接口的类之后,你可以在类中实例化一个线程对象。

Thread 定义了几个构造方法,下面的这个是我们经常使用的:

Thread(Runnable threadOb,String threadName);
这里,threadOb 是一个实现 Runnable 接口的类的实例,并且 threadName 指定新线程的名字。

新线程创建之后,你调用它的 start() 方法它才会运行。

void start();
下面是一个创建线程并开始让它执行的实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class RunnableDemo implements Runnable {
private Thread t;
private String threadName;

RunnableDemo( String name) {
threadName = name;
System.out.println("Creating " + threadName );
}

public void run() {
System.out.println("Running " + threadName );
try {
for(int i = 4; i > 0; i--) {
System.out.println("Thread: " + threadName + ", " + i);
// 让线程睡眠一会
Thread.sleep(50);
}
}catch (InterruptedException e) {
System.out.println("Thread " + threadName + " interrupted.");
}
System.out.println("Thread " + threadName + " exiting.");
}

public void start () {
System.out.println("Starting " + threadName );
if (t == null) {
t = new Thread (this, threadName);
t.start ();
}
}
}

public class TestThread {

public static void main(String args[]) {
RunnableDemo R1 = new RunnableDemo( "Thread-1");
R1.start();

RunnableDemo R2 = new RunnableDemo( "Thread-2");
R2.start();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.

通过继承Thread来创建线程

创建一个线程的第二种方法是创建一个新的类,该类继承 Thread 类,然后创建一个该类的实例。

继承类必须重写 run() 方法,该方法是新线程的入口点。它也必须调用 start() 方法才能执行。

该方法尽管被列为一种多线程实现方式,但是本质上也是实现了 Runnable 接口的一个实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class ThreadDemo extends Thread {
private Thread t;
private String threadName;

ThreadDemo( String name) {
threadName = name;
System.out.println("Creating " + threadName );
}

public void run() {
System.out.println("Running " + threadName );
try {
for(int i = 4; i > 0; i--) {
System.out.println("Thread: " + threadName + ", " + i);
// 让线程睡眠一会
Thread.sleep(50);
}
}catch (InterruptedException e) {
System.out.println("Thread " + threadName + " interrupted.");
}
System.out.println("Thread " + threadName + " exiting.");
}

public void start () {
System.out.println("Starting " + threadName );
if (t == null) {
t = new Thread (this, threadName);
t.start ();
}
}
}

public class TestThread {

public static void main(String args[]) {
ThreadDemo T1 = new ThreadDemo( "Thread-1");
T1.start();

ThreadDemo T2 = new ThreadDemo( "Thread-2");
T2.start();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.

Thread 方法

实例

如下的ThreadClassDemo 程序演示了Thread类的一些方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 文件名 : DisplayMessage.java
// 通过实现 Runnable 接口创建线程
public class DisplayMessage implements Runnable {
private String message;

public DisplayMessage(String message) {
this.message = message;
}

public void run() {
while(true) {
System.out.println(message);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 文件名 : GuessANumber.java
// 通过继承 Thread 类创建线程

public class GuessANumber extends Thread {
private int number;
public GuessANumber(int number) {
this.number = number;
}

public void run() {
int counter = 0;
int guess = 0;
do {
guess = (int) (Math.random() * 100 + 1);
System.out.println(this.getName() + " guesses " + guess);
counter++;
} while(guess != number);
System.out.println("** Correct!" + this.getName() + "in" + counter + "guesses.**");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 文件名 : ThreadClassDemo.java
public class ThreadClassDemo {

public static void main(String [] args) {
Runnable hello = new DisplayMessage("Hello");
Thread thread1 = new Thread(hello);
thread1.setDaemon(true);
thread1.setName("hello");
System.out.println("Starting hello thread...");
thread1.start();

Runnable bye = new DisplayMessage("Goodbye");
Thread thread2 = new Thread(bye);
thread2.setPriority(Thread.MIN_PRIORITY);
thread2.setDaemon(true);
System.out.println("Starting goodbye thread...");
thread2.start();

System.out.println("Starting thread3...");
Thread thread3 = new GuessANumber(27);
thread3.start();
try {
thread3.join();
}catch(InterruptedException e) {
System.out.println("Thread interrupted.");
}
System.out.println("Starting thread4...");
Thread thread4 = new GuessANumber(75);

thread4.start();
System.out.println("main() is ending...");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
运行结果如下,每一次运行的结果都不一样。

Starting hello thread...
Starting goodbye thread...
Hello
Hello
Hello
Hello
Hello
Hello
Goodbye
Goodbye
Goodbye
Goodbye
Goodbye
.......

通过 Callable 和 Future 创建线程

  1. 创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。

  2. 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。

  3. 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。

  4. 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class CallableThreadTest implements Callable<Integer> {
public static void main(String[] args)
{
CallableThreadTest ctt = new CallableThreadTest();
FutureTask<Integer> ft = new FutureTask<>(ctt);
for(int i = 0;i < 100;i++)
{
System.out.println(Thread.currentThread().getName()+" 的循环变量i的值"+i);
if(i==20)
{
new Thread(ft,"有返回值的线程").start();
}
}
try
{
System.out.println("子线程的返回值:"+ft.get());
} catch (InterruptedException e)
{
e.printStackTrace();
} catch (ExecutionException e)
{
e.printStackTrace();
}

}
@Override
public Integer call() throws Exception
{
int i = 0;
for(;i<100;i++)
{
System.out.println(Thread.currentThread().getName()+" "+i);
}
return i;
}
}

创建线程的三种方式的对比

  1. 采用实现 Runnable、Callable 接口的方式创建多线程时,线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类。

  2. 使用继承 Thread 类的方式创建多线程时,编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread() 方法,直接使用 this 即可获得当前线程。

Java URL处理

Posted on 2019-06-11 | In java , 基础知识 |
Words count in article: 617 | Reading time ≈ 2

Java URL处理

URL(Uniform Resource Locator)中文名为统一资源定位符,有时也被俗称为网页地址。表示为互联网上的资源,如网页或者FTP地址

URL可以分为如下几个部分。

protocol://host:port/path?query#fragment
protocol(协议)可以是 HTTP、HTTPS、FTP 和 File,port 为端口号,path为文件路径及文件名。

HTTP 协议的 URL 实例如下:

http://www.runoob.com/index.html?language=cn#j2se

URL 解析:

  • 协议为(protocol):http
  • 主机为(host:port):www.runoob.com
  • 端口号为(port): 80 ,以上URL实例并未指定端口,因为 HTTP 协议默认的端口号为 80。
  • 文件路径为(path):/index.html
  • 请求参数(query):language=cn
  • 定位位置(fragment):j2se,定位到网页中 id 属性为 j2se 的 HTML 元素位置 。

URL 类方法

在java.net包中定义了URL类,该类用来处理有关URL的内容。

实例

演示了使用java.net的URL类获取URL的各个部分参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.net.*;
import java.io.*;

public class URLDemo
{
public static void main(String [] args)
{
try
{
URL url = new URL("http://www.runoob.com/index.html?language=cn#j2se");
System.out.println("URL 为:" + url.toString());
System.out.println("协议为:" + url.getProtocol());
System.out.println("验证信息:" + url.getAuthority());
System.out.println("文件名及请求参数:" + url.getFile());
System.out.println("主机名:" + url.getHost());
System.out.println("路径:" + url.getPath());
System.out.println("端口:" + url.getPort());
System.out.println("默认端口:" + url.getDefaultPort());
System.out.println("请求参数:" + url.getQuery());
System.out.println("定位位置:" + url.getRef());
}catch(IOException e)
{
e.printStackTrace();
}
}
}
1
2
3
4
5
6
7
8
9
10
URL 为:http://www.runoob.com/index.html?language=cn#j2se
协议为:http
验证信息:www.runoob.com
文件名及请求参数:/index.html?language=cn
主机名:www.runoob.com
路径:/index.html
端口:-1
默认端口:80
请求参数:language=cn
定位位置:j2se

URLConnections 类方法

openConnection() 返回一个 java.net.URLConnection。

例如:

如果你连接HTTP协议的URL, openConnection() 方法返回 HttpURLConnection 对象。

如果你连接的URL为一个 JAR 文件, openConnection() 方法将返回 JarURLConnection 对象。

等等…

实例

以下实例中URL采用了HTTP 协议。 openConnection 返回HttpURLConnection对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.net.*;
import java.io.*;
public class URLConnDemo
{
public static void main(String [] args)
{
try
{
URL url = new URL("http://www.runoob.com");
URLConnection urlConnection = url.openConnection();
HttpURLConnection connection = null;
if(urlConnection instanceof HttpURLConnection)
{
connection = (HttpURLConnection) urlConnection;
}
else
{
System.out.println("请输入 URL 地址");
return;
}
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String urlString = "";
String current;
while((current = in.readLine()) != null)
{
urlString += current;
}
System.out.println(urlString);
}catch(IOException e)
{
e.printStackTrace();
}
}
}
1
2
3
$ javac URLConnDemo.java 
$ java URLConnDemo
.....这里会输出菜鸟教程首页(http://www.runoob.com)的 HTML 内容.....

Java 网络编程

Posted on 2019-06-11 | In java , 基础知识 |
Words count in article: 1.3k | Reading time ≈ 5

Java 网络编程

网络编程是指编写运行在多个设备(计算机)的程序,这些设备都通过网络连接起来。

java.net 包中 J2SE 的 API 包含有类和接口,它们提供低层次的通信细节。你可以直接使用这些类和接口,来专注于解决问题,而不用关注通信细节。

java.net 包中提供了两种常见的网络协议的支持:

  • TCP:TCP 是传输控制协议的缩写,它保障了两个应用程序之间的可靠通信。通常用于互联网协议,被称 TCP / IP。

  • UDP:UDP 是用户数据报协议的缩写,一个无连接的协议。提供了应用程序之间要发送的数据的数据包。

  • Socket 编程:这是使用最广泛的网络概念。

  • URL 处理:这部分会在另外的篇幅里讲

Socket 编程

TCP用主机的IP地址加上主机上的端口号作为TCP连接的端点,这种端点就叫做套接字(socket)或插口。

套接字使用TCP提供了两台计算机之间的通信机制。 客户端程序创建一个套接字,并尝试连接服务器的套接字。

当连接建立时,服务器会创建一个 Socket 对象。客户端和服务器现在可以通过对 Socket 对象的写入和读取来进行通信。

java.net.Socket 类代表一个套接字,并且 java.net.ServerSocket 类为服务器程序提供了一种来监听客户端,并与他们建立连接的机制。

以下步骤在两台计算机之间使用套接字建立TCP连接时会出现:

  • 服务器实例化一个 ServerSocket 对象,表示通过服务器上的端口通信。

  • 服务器调用 ServerSocket 类的 accept() 方法,该方法将一直等待,直到客户端连接到服务器上给定的端口。

  • 服务器正在等待时,一个客户端实例化一个 Socket 对象,指定服务器名称和端口号来请求连接。

  • Socket 类的构造函数试图将客户端连接到指定的服务器和端口号。如果通信被建立,则在客户端创建一个 Socket 对象能够与服务器进行通信。

  • 在服务器端,accept() 方法返回服务器上一个新的 socket 引用,该 socket 连接到客户端的 socket。

连接建立后,通过使用 I/O 流在进行通信,每一个socket都有一个输出流和一个输入流,客户端的输出流连接到服务器端的输入流,而客户端的输入流连接到服务器端的输出流。

TCP 是一个双向的通信协议,因此数据可以通过两个数据流在同一时间发送.

Socket 客户端实例

如下的 GreetingClient 是一个客户端程序,该程序通过 socket 连接到服务器并发送一个请求,然后等待一个响应。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 文件名 GreetingClient.java

import java.net.*;
import java.io.*;

public class GreetingClient
{
public static void main(String [] args)
{
String serverName = args[0];
int port = Integer.parseInt(args[1]);
try
{
System.out.println("连接到主机:" + serverName + " ,端口号:" + port);
Socket client = new Socket(serverName, port);
System.out.println("远程主机地址:" + client.getRemoteSocketAddress());
OutputStream outToServer = client.getOutputStream();
DataOutputStream out = new DataOutputStream(outToServer);

out.writeUTF("Hello from " + client.getLocalSocketAddress());
InputStream inFromServer = client.getInputStream();
DataInputStream in = new DataInputStream(inFromServer);
System.out.println("服务器响应: " + in.readUTF());
client.close();
}catch(IOException e)
{
e.printStackTrace();
}
}
}

Socket 服务端实例

如下的GreetingServer 程序是一个服务器端应用程序,使用 Socket 来监听一个指定的端口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// 文件名 GreetingServer.java

import java.net.*;
import java.io.*;

public class GreetingServer extends Thread
{
private ServerSocket serverSocket;

public GreetingServer(int port) throws IOException
{
serverSocket = new ServerSocket(port);
serverSocket.setSoTimeout(10000);
}

public void run()
{
while(true)
{
try
{
System.out.println("等待远程连接,端口号为:" + serverSocket.getLocalPort() + "...");
Socket server = serverSocket.accept();
System.out.println("远程主机地址:" + server.getRemoteSocketAddress());
DataInputStream in = new DataInputStream(server.getInputStream());
System.out.println(in.readUTF());
DataOutputStream out = new DataOutputStream(server.getOutputStream());
out.writeUTF("谢谢连接我:" + server.getLocalSocketAddress() + "\nGoodbye!");
server.close();
}catch(SocketTimeoutException s)
{
System.out.println("Socket timed out!");
break;
}catch(IOException e)
{
e.printStackTrace();
break;
}
}
}
public static void main(String [] args)
{
int port = Integer.parseInt(args[0]);
try
{
Thread t = new GreetingServer(port);
t.run();
}catch(IOException e)
{
e.printStackTrace();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
编译以上两个 java 文件代码,并执行以下命令来启动服务,使用端口号为 6066:

$ javac GreetingServer.java
$ java GreetingServer 6066
等待远程连接,端口号为:6066...
新开一个命令窗口,执行以上命令来开启客户端:

$ javac GreetingClient.java
$ java GreetingClient localhost 6066
连接到主机:localhost ,端口号:6066
远程主机地址:localhost/127.0.0.1:6066
服务器响应: 谢谢连接我:/127.0.0.1:6066
Goodbye!

setSoTimeout
public void setSoTimeout(int timeout)
throws SocketException启用/禁用带有指定超时值的 SO_TIMEOUT,以毫秒为单位。将此选项设为非零的超时值时,在与此 Socket 关联的 InputStream 上调用 read() 将只阻塞此时间长度。
如果超过超时值,将引发 java.net.SocketTimeoutException,虽然 Socket 仍旧有效。选项必须在进入阻塞操作前被启用才能生效。
超时值必须是 > 0 的数。超时值为 0 被解释为无穷大超时值。

1…212223…38
zzm99

zzm99

372 posts
40 categories
3 tags
GitHub
0%
© 2020 zzm99 | Site words total count: 409.1k
Powered by Hexo
|
Theme — NexT.Gemini v5.1.4