Java Basics Full Article
Java SE 总结
一、Java 体系
1、Java 基础
1、跨平台
2、面向对象编程语言
3、分布式计算
2、Java 的运行机制
- 编程 Java 程序
- 编译 Java 文件
- JVM 读取字节码文件运行程序
3、Java 的三大体系
- Java SE(J2SE)
- Java ME(J2ME)
- Java EE(J2EE)
4、配置 Java 环境
JRE、JDK
JRE:Java Runtime Environment Java 运行环境
JDK:Java Devlopment Kit Java 开发工具包
5、开发
- 编译
javac HelloWorld.java
- 运行
java HelloWorld
Java IDE
NetBeans、Eclipse、IDEA
6、代码规范
- 强制性代码规范,必须执行的
1、Java 程序的文件名与类名必须一致,若不一致,无法通过编译。
2、main 方法是程序的入口,方法的定义必须严格按照格式书写。
3、类是组织 Java 代码结构的,类中的方法是执行具体业务的。
- 非强制性代码规范,建议按照此方式编写代码
1、一行只写一条语句。
2、在 1 的基础上,还要注意代码缩进。
二、基本概念
1、安装 Java 环境
- 编写 Java 代码
- 编译 Java 代码,成为字节码文件 16 进制
javac 文件名(带后缀) - 让 JVM 执行字节码文件,运行程序
java 文件名(不带后缀)
IDE:集成开发环境
Eclipse、IDEA
2、注释
注释就是用通俗易懂的语言对代码进行描述解释,方便自己和他人阅读。
- 单行注释:
//注释内容
- 多行注释:
/*注释内容注释内容注释内容*/
- 文档注释:
/**注释内容*注释内容*注释内容*/
3、关键字
Java 语言预先定义好的,有指定意义的标识符,组成程序的基本元素。
4、变量
- 数据类型
- 变量名
- 变量值
基本数据类型
+引用数据类型
5、使用变量
1、声明变量的数据类型和变量名(包含数字、字母、下划线、$,不能包含空格、运算符,不能用关键字命名,不能以数字开头),大小写可以混用,首单词应该小写,后续单词的首字母大写。
userId、studentName (驼峰式命名法)
2、给内存空间赋值,该值就是变量值。
6、Java 的数据类型
- 基本数据类型
byte、int、short、long、float、double、char、boolean
数值类型(整数、小数)
byte 1 个字节 (8 位)
int 4 个字节(32 位)
short 2 个字节(16 位)
long 8 个字节(64 位)
float 4 个字节(32 位) 单精度浮点型
double 8 个字节(64 位)双精度浮点型
非数值类型(文本)
char 2 个字节(16 位)
boolean 1 个字节 (8 位)、判断逻辑是否成立 true 1/false 0
- 引用数据类型
7、数据类型转换
- 自动转换
Java 可以自动对某些数据类型进行自动转换。
规则:只能由低字节向高字节进行转换,反之则不行。
byte->short->int->long->float->double
- 强制类型转换
Java 无法自动转换的数据类型,开发者可以通过强制手段进行转换。
一般来讲强制类型转换可能会造成精度损失。
double num = 10.0;
int num2 = (int)num;
8、运算符
- 赋值运算符
数据类型 变量名 = 数值 / 变量;
//1、创建变量用来记录张三的体重
double weight1 = 70.5;
//2、创建变量表示李四的体重
double weight2 = 60.5;
System.out.println("交换之前:张三的体重是"+weight1+",李四的体重是"+weight2);
System.out.println("进行交换");double temp = weight1;weight1 = weight2;
weight2 = temp;
System.out.println("交换之后:张三的体重是"+weight1+",李四的体重是"+weight2);
- 算术运算符
- 基本算术运算符
+、-、*、/、%、++、–
变量 1 + 变量 2
变量 1 - 变量 2
变量 1 * 变量 2
变量 1 / 变量 2
变量 1 % 变量 2
变量 ++、++ 变量
变量–、– 变量
变量 ++:先操作,再运算。
++ 变量:先运算,再操作。 - 复合算术运算符
+=、-=、*=、/=、%=
变量 1 += 变量 2:先求出变量 1 和变量 2 之和,再把计算结果赋值给变量 1,变量 1 = 变量 1 + 变量 2
- 基本算术运算符
- 关系运算符
==、!=、>、<、>=、<= - 逻辑运算符
逻辑运算符只能用于 boolean 类型的数据运算,判断 boolean 数据之间的逻辑关系,与、或、非。
&(与)、|(或)、!(非)、&&(短路与)、||(短路或)参与逻辑运算符的变量都是 boolean 的。
1、变量 1 & 变量 2:只有当变量 1 和变量 2 都为 true,结果为 true,否则为 false。 【A & B AB 都会执行】
2、变量 1 | 变量 2:变量 1 和变量 2 只要有一个为 true,结果为 true,否则为 false。【A | B AB 都会执行】
3、! 变量 1:若变量 1 为 true,结果为 false,若变量 1 为 false,结果为 true。
4、变量 1 && 变量 2:只有当变量 1 和变量 2 都为 true,结果为 true,否则为 false。 【A && B 若 A 为 false,B 不执行】
5、变量 1 || 变量 2:变量 1 和变量 2 只要有一个为 true,结果为 true,否则为 false。【A || B 若 A 为 true,B 不执行】
int num1 = 10;
int num2 = 11;
System.out.println((++num1==num2)||(num1++==num2));
System.out.println(num1);
int num1 = 10;
int num2 = 11;
System.out.println((++num1==num2)|(num1++==num2));
System.out.println(num1);
int num1 = 10;
int num2 = 11;
System.out.println((num1++==num2)&(++num1==num2));
System.out.println(num1);
int num1 = 10;int num2 = 11;
System.out.println((num1++==num2)&&(++num1==num2));
System.out.println(num1);
9、条件运算符
三元运算符、三目运算符、三元表达式
根据不同的条件给同一个变量赋不同的值,变量 = 条件? 值 1: 值 2.
10、位运算符
- 十进制和二进制的转换
十进制转二进制:目标数除以 2,若能除尽,该位记做 0,若除不尽,该位记做 1,再对商继续除以 2,以此类推,直到商为 0,然后把每一位的结果反序组合就是对应的二进制。
10:1010
17:10001
二进制转十进制:从目标数的最后侧起,本位的数值乘以本位的权重,权重就是 2 的第几位的位数减一次方,将每一位的值进行相加,得到的结果就是对应的十进制。
位运算符:
- &(按位与)
- |(按位或)
- ^(按位异或)
- <<(左移)、>>(右移)
变量 1 & 变量 2:先把变量 1 和变量 2 转为二进制,每一位的数字一一对应,进行比较判断,若都为 1,则该位记做 1,否则记做 0。
变量 1 | 变量 2:先把变量 1 和变量 2 转为二进制,每一位的数字一一对应,进行比较判断,只要有一个为 1,则该位记做 1,否则记做 0。
变量 1 ^ 变量 2:先把变量 1 和变量 2 转为二进制,每一位的数字一一对应,进行比较判断,相同记做 0,不同记做 1。
变量 1 << 变量 2:变量 1 乘以 2 的变量 2 次方
2 << 3 : 2 * 8 = 16
变量 1 >> 变量 2:变量 1 除以 2 的变量 2 次方
2 >> 3:2/8 = 0
11、运算符的优先级
!> 算术运算符 > 关系运算符 > 逻辑运算符 (&&>||)
三、流程控制
1、选择流程控制
- if else
用来判断某个条件是否成立,然后执行不同的逻辑运算。
基本语法:
if(判断条件){
//条件成立的代码
}else{
//条件不成立的代码
}
- 多重 if
173 M173~178 L178 XL
- if 后面必须跟条件
- else 后面不能跟条件
- else 后面可以根据 {},也可以跟 if
2、循环流程控制
- switch-case
与 if 不同的是,switch-case 只能完成等值判断,而无法完成判断大小。
如果是判断两个值是否相等,可以使用 switch-case,如果比较两个值的大小关系,则不能使用 switch-case。
switch 支持 int、short、byte、char、枚举、String 类型,不支持 boolean 类型。
基本语法
switch(变量){
case 值1:
//业务代码
break;
case 值2:
//业务代码
break;
...
default:
//业务代码
break;
}
case 判断变量是否等于某个值,default 表示所有的 case 都不成立的情况下所执行的代码。
- 1 奖励 2000
- 2 奖励 1000
- 3 奖励 500
- 否则没有奖励
public static void main(String[] args) {
int placing = 1;
if(placing == 1) {
System.out.println("奖励2000元");
}else {
if(placing == 2) {
System.out.println("奖励1000元");
}else {
if(placing == 3) {
System.out.println("奖励500元");
}else{
System.out.println("没有奖励");
}
}
}
switch(placing) {
case 1:
System.out.println("奖励2000元");
break;
case 2:
System.out.println("奖励1000元");
break;
case 3:
System.out.println("奖励500元");
break;
default:
System.out.println("没有奖励");
break;
}
}
3、循环
for、while、do-while、foreach
循环四要素:
- 初始化循环变量
- 循环条件
- 循环体
- 更新循环变量
while 初始化循环变量
while(循环条件){
循环体
更新循环变量
}
//初始化循环变量
int num = 0;
//循环条件
while(num < 10) {
//循环体
System.out.println("Hello World");
//更新循环变量
num++;
}
int num = 0;
String flag = "y";
while(flag.equals("y")) {
System.out.print("请输入学生学号:");
Scanner scanner = new Scanner(System.in);
int id = scanner.nextInt();
switch(id) {
case 1:
System.out.println("张三的成绩是96");
break;
case 2:
System.out.println("李四的成绩是91");
break;
case 3:
System.out.println("王五的成绩是89");
break;
default:
System.out.println("请输入正确的学号");
break;
}
System.out.print("是否继续?y/n");
flag = scanner.next();
}
System.out.println("感谢使用学生成绩查询系统");
do-while
//初始化循环变量
int num = 0;
do {
//循环体
System.out.println("Hello World");
//更新循环变量
num++;
}while(num<10);
//循环条件
Scanner scanner = new Scanner(System.in);
String result = "";
do {
System.out.println("张三参加体能测试,跑1000米");
System.out.print("是否合格?y/n");
result = scanner.next();
}while(result.equals("n"));
System.out.println("合格,通过测试");
//for
for(初始化循环变量;循环条件;更新循环变量){
循环体
}
for(int num = 0;num < 10;num++) {
System.out.println("Hello World");
}
while、do-while、for 3 种循环的区别
- 相同点:都遵循循环四要素,初始化循环变量、循环条件、循环体、更新循环变量。
- 不同点:
- while 和 do-while 适用于循环次数不确定的业务场景;for 适用于循环次数确定的场景。
- while 和 for 都是先判断循环条件,再执行循环体;do-while 先执行循环体,再判断循环条件。
分别使用 while、do-while、for 循环输出 10 以内的所有奇数。
//while循环
int num = 0;
while(num <= 10) {
if(num%2!=0) {
System.out.println(num);
}
num++;
}
//do-while循环
int num = 0;
do {
if(num%2!=0) {
System.out.println(num);
}
num++;
}while(num <= 10);
//for循环
for(int num = 0;num <= 10;num++) {
if(num%2!=0) {
System.out.println(num);
}
}
for 循环只适用于循环次数确定的场景下 (for 也可以适用于循环次数不确定的场景,只不过一般不会用这种方式进行开发),while 和 do-while 循环次数确定或者不确定都可以使用。
String result = "n";
for(;result.equals("n");
) {
System.out.println("张三参加体能测试,跑1000米");
System.out.print("是否合格?y/n");
result = scanner.next();
}
System.out.println("合格,通过测试");
四、数组
1、数组
数组就是一种可以存储大量数据类型相同的变量的数据结构,数组就是一个具有相同数据类型的数据集合。
数组中的数据必须是同一种数据类型的。
2、数组的基本要素
- 数组名称
- 数组元素
- 元素下标
- 数据类型
数组本身就是一个变量,数组名称就是变量名,数组中保存的每一个数据都会有一个下标(从 0 开始)
//声明数组
int[] array;
//分配内存空间
array = new int[6];
//给数组赋值
array[0] = 1;
array[1] = 2;
array[2] = 3;
array[3] = 4;
array[4] = 5;
array[5] = 6;
int[] array2 = {1,2,3,4,5,6};
int[] array3 = new int[]{1,2,3,4,5,6};
3、数组常见的错误
- 数据类型不匹配。
- 边声明边赋值的方式,代码必须写在同一行,不能换行。
- 数组下标越界。
4、数组的常用操作及方法
- 求数组的最大值
- 求数组的最小值
- 在数组的指定位置插入数据
- 对数组进行排序
5、二维数组
二维数组简单理解即一维数组中保存的值是另外一个一维数组。
变量、数据类型、流程控制、循环、数组。
用户管理系统
- 查询用户:将系统中保存的全部用户信息在控制台打印输出。
- 添加用户:向系统中添加新的用户信息,如果添加的用户已经存在,给出提示信息。
- 删除用户:输入用户名,进行删除操作,若输入的用户名不存在,给出提示信息。
- 账号冻结:输入用户名,进行冻结操作,若输入的用户名不存在或者该用户已经被冻结,给出相应提示。
- 账号解冻:输入用户名,进行解封操作,若输入的用户名不存在或者该用户状态正常,给出相应提示。
- 退出系统:跳出循环,给出提示信息。
五、面向对象程序
1、面向对象
面向对象编程思想:将程序模块化的思想。
- 什么是面向对象?
面向对象编程思想诞生之前,程序开发采用的是面向过程的结构化编程方式,是一种面向功能划分的软件结构。
最小粒度细化到方法这一层。
面向过程注重的是每一个步骤,面向对象关注点在于整件事情的模块化结构。
- 类和对象
类和对象的关系
每个对象都有特定的特征:1、属性。2、方法。
属性指的是对象的静态特征,方法用来描述对象的动态特征。
对象是用来描述客观存在的一个实体,改实体是由一组属性和方法构成。
类是与对象紧密结合的另外一个概念,类是产生对象的模版,所有的对象都是通过类来创建的。
二者的关系:类是对象的抽象化描述,这些对象具有相同的特征和动作(属性和方法)。
对象是类的具体实例。
Java 程序是以类位组织单元,程序运行时的主体是通过类创建的具体对象。
三大特征:封装、继承、多态
2、定义类
public class ClassName{
//定义属性,属性名符合驼峰式命名法
public DataType properityName;
//定义方法,方法名符合驼峰式命名法
public ReturnDataType methodName(参数列表:数据类型 参数名){
//方法体
}
}
Java 关于返回值的定义分为两类:有返回值和无返回值,有返回值的方法需要在方法定义时指定返回值的数据类型,并在方法体中用 return 将结果返回给外部调用者,加法运算。
如果一个方法不需要进行返回操作,将返回值类型定义为 void。
参数列表是指外部在调用该方法时需要传入到方法内部进行运算的数据。
3、构造函数、构造方法、构造器
构造函数是一种特殊的方法,普通方法是用来描述某个动作的,构造方法是用来创建对象的。
- 方法名必须与类名一致。
- 不需要定义返回值类型。
构造函数可分为有参构造和无参构造,有参构造是指带参数的构造函数,无参构造是指没有参数的构造函数。
任何一个类都默认自带一个无参构造函数,如果手动在类中定义一个有参构造,则会覆盖默认的无参构造。
4、this 关键字
this 用来指代当前类的实例化对象,通过 this 可以调用当前类的属性和方法,比如在有参构造中,通过 this 将外部传入的值赋给当前类的实例化对象。
this 除了可以在类中访问属性也可以在类中调用方法,类中的方法可以分为两类:构造方法、普通方法,用 this 调用这两类方法的语法也不同。
1、调用构造函数的语法是 this(参数列表),不能在普通方法中使用 this 调用构造函数。
2、用 this 调用普通方法,this. 方法名 (参数列表),可以在构造函数中使用,也可以在普通方法中使用。
5、成员变量和局部变量
变量的作用域是指在程序中可以通过变量名来访问该变量的范围,变量的作用域由变量被声明时所在位置决定的,Java 中根据不同的作用域可以将变量分为成员变量和局部变量。
局部变量:如果一个变量在方法中声明,则该变量是局部变量。
成员变量:如果一个变量在方法外,类中声明,则该变量是成员变量。
public class HelloWorld{
int num2 = 2;
public int test(){
int num1 = 1;
}
}
1、成员变量和局部变量的区别在于作用域不同,成员变量的作用域在整个类中,类中的每个方法都可以访问该变量,局部变量的作用域只在定义该变量的方法中,出了方法体就无法访问。
2、成员变量和局部变量的初始值也不同,局部变量不会赋初始值,成员变量会赋初始值,具体的值是由成员变量的数据类型决定的。
6、封装
封装是指将类的属性隐藏在内部,外部不能直接访问和修改,如何实现?通过修改成员变量的可见性,从公有改为私有。
public class Student {
private int id;
private String name;
private int age;
public void show() {
System.out.println("学生信息如下:"); System.out.println("学生编号:"+id); System.out.println("学生姓名:"+name); System.out.println("学生年龄:"+age); } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { if(age <= 0) { System.out.println("输入的数值有误!"); age = 18; } this.age = age; }}
封装的核心思想就是尽可能把属性都隐藏在内部,对外提供方法来访问,我们可以在这些方法中添加逻辑处理来实现过滤,以屏蔽错误数据的赋值。
封装的步骤:
- 修改属性(成员变量)的访问权限为私有,使得外部不能直接访问。
- 提供外部可以直接调用的方法。
- 在该方法中加入对于属性的逻辑控制,避免出现逻辑上的错误。
什么是访问权限?
访问权限是指该属性可以被直接访问的范围,是在属性定义时设定的,访问权限的可选项一共有 4 种:区别在于作用域范围不同。
- public
- private
- 默认(不写)
- protected
7、static
static 表示静态或者全局,可以用来修饰成员变量和成员方法以及代码块。
使用 static 修饰的成员变量和成员方法独立于该类的任何一个实例化对象,访问时不依赖于该类的对象,而是直接通过类去访问,可以理解为被该类的所有实例对象所共用,所以说是全局的。
static 还可以修饰代码块,被 static 修饰的代码块叫做静态代码块。
static { System.out.println(1);}
静态代码块的特点是只执行一次,什么时候执行?当这个类被加载到内存时执行,不需要开发者手动调用,会自动执行。
被加载到内存中的类叫做运行时类,静态代码块就是在家中类的时候执行的,因为类只加载一次,所以静态代码块也只执行一次。
8、继承
- 什么是继承?
继承是用来描述类之间的关系的,即一个类继承(拥有)另外一个类中的属性和方法,被继承的类叫做父类,继承父类的类叫做子类。
继承的基本语法:
public class 类名 extends 父类名{}public class People { private int id; private String name; private int age; private char gender; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public char getGender() { return gender; } public void setGender(char gender) { this.gender = gender; }}public class Student extends People {}
Java 中的继承是单继承,也就是说一个子类只能有一个直接父类。
8.1、子类访问父类
创建一个子类对象的时候,会默认先创建一个父类对象,无论是通过有参构造或是无参构造来创建子类对象,都是通过【无参构造来创建父类对象的】。
可以通过 super 关键字让子类创建对象时调用父类的有参构造。
public Student() { super(1); System.out.println("通过无参构造创建了Student对象");}
子类可以访问父类的构造方法、普通方法、成员变量,都是通过 super 关键字来完成,具体语法:
构造方法:super(参数列表)
普通方法:super. 方法名 (参数列表)
成员变量:super. 成员变量名
在子类的构造方法中,可以通过 super 访问父类的构造方法和普通方法。
在子类的普通方法中,只能通过 super 访问父类的普通方法。
8.2、子类的访问权限
访问权限修饰符:public、protected、默认修饰符、private。
包:package,用来管理 Java 文件,一个项目中不可避免会出现同名的 Java 类,为了防止产生冲突,可以把同名的 Java 类分别放入不同的包中。
包的命名规范:包名由小写字母组成,不能以 . 开头或结尾,可以包含数字,但不能以数字开头,使用 . 来分层。
包的命名方式一般采用网络域名的反向输出,如 com.company.test/com.company.entity。
9、方法重写
子类在继承父类方法的基础上,对父类方法重新定义并覆盖的操作叫做方法重写。构造方法不能被重新,方法重写的规则:
1、父子类的方法名相同。
2、父子类的方法参数列表相同。
3、子类方法的返回值与父类方法返回值类型相同或者是其子类。
4、子类方法的访问权限不能小于父类。
10、方法重写 VS 方法重载
位置:方法重写在子类中对父类方法进行重写,方法重载是在同一个类中。
方法名:方法重写相同,方法重载相同。
参数列表:方法重写相同,方法重载不同。
返回值:方法重写相同或是其子类,方法重载没有要求。
访问权限:方法重写不能小于父类,方法重载没有要求。
11、多态
一个事物具有多种表现形态,在 Java 程序中,定义一个方法,在具体的生成环境中根据不同的需求呈现不同的业务逻辑,多态的前提是继承。
public class Memeber { public void buyBook() { }}//子类一public class OrdinaryMember extends Memeber { public void buyBook() { System.out.println("普通会员买书打9折"); }}//子类二public class SuperMember extends Memeber { public void buyBook() { System.out.println("超级会员买书打6折"); }}public class Cashier { private Memeber memeber; public Memeber getMemeber() { return memeber; } public void setMemeber(Memeber memeber) { this.memeber = memeber; } public void settlement() { this.memeber.buyBook(); }}public class Test { public static void main(String[] args) { OrdinaryMember ordinaryMember = new OrdinaryMember(); SuperMember superMember = new SuperMember(); Cashier cashier = new Cashier(); cashier.setMemeber(superMember); cashier.settlement(ordinaryMember); }}
多态的具体使用有两种形式:
1、定义方法时形参类型为父类,实际调用方法时传入子类类型的参数。
2、定义方法时返回值类型为父类,实际调用方法时返回子类对象。
以上两种形式的基本原理都是父类引用可以指向子类对象。
11、抽象方法和抽象类
如果一个方法只有方法的声明而没有具体的方法实现,这个方法就叫做抽象方法,Java 中的抽象方法需要使用 abstract 关键字来修饰。
public abstract void buyBook();
一旦类中定义了抽象方法,则该类也必须声明为抽象类,需要在类定义处添加 abstract 关键字。
public abstract class Member { public abstract void buyBook();}
抽象类与普通类的区别是抽象类不能被实例化,抽象方法与普通方法的区别是抽象方法没有方法体。
抽象类中可以没有抽象方法,但是包含了抽象方法的类必须定义为抽象类。即我们可以在抽象类中定义普通方法,但是在普通类中不能定义抽象方法。
如果父类是抽象类,一旦子类继承了该抽象父类,则子类必须对父类的抽象方法进行重写,否则程序报错。
public abstract class Member { public abstract void buyBook();}package com.southwind.test;public class SuperMember extends Member { @Override public void buyBook() { // TODO Auto-generated method stub System.out.println("超级会员买书打6折"); }}
如果子类也是抽象类,则可以不用重写父类的抽象方法。
12、接口
- 什么是接口?
接口是由抽象类衍生出来的一个概念,并由此产生了一种编程方式:面向接口编程。
面向接口编程就是将程序中的业务模块进行分离,以接口的形式去对接不同的业务模块。
面向接口编程的优点:当用户需求变更时,只需要切换不同的实现类,而不需要修改串联模块的接口,减少对系统的影响。
1、能够最大限度实现解耦合,降低程序的耦合性。
2、使程序易于扩展。
3、有利于程序的后期维护。
- 如何使用接口
接口在 Java 中时独立存在的一种结构,和类相似,我们需要创建一个接口文件,Java 中用 class 关键字来标识类,用 interface 来标识接口,基本语法:
public interface 接口名{ public 返回值 方法名(参数列表)}
接口其实就是一个抽象类,极度抽象的抽象类。
抽象类:一个类中一旦存在没有具体实现的抽象方法时,那么该类就必须定义为抽象类,同时抽象类允许存在非抽象方法。
但是接口完全不同,接口中不能存在非抽象方法,接口中必须全部是抽象方法。
因为接口中必须全部都是抽象方法,所以修饰抽象方法的关键字 abstract 可以省略。
接口中允许定义成员变量,但是有如下要求:
1、不能定义 private 和 protected 修饰的成员变量,只能定义public 和默认访问权限修饰符修饰的成员变量。
2、接口中的成员变量在定义时就必须完成初始化。
3、接口中的成员变量都是静态常量,即可以直接通过接口访问,同时值不能被修改。
package com.southwind.test;public interface MyInterface { public int ID = 0; String NAME = "张三"; public void test();}
使用接口时,不能直接实例化接口对象,而必须实例化其实现类对象,实现类本身就是一个普通的 Java 类,创建实现类的代码如下所示。
package com.southwind.test;public class MyInterfaceImpl implements MyInterface { @Override public void test() { // TODO Auto-generated method stub }}
通过 implements 关键字来指定实现类具体要实现的接口,在实现类的内部需要对接口的所有抽象方法进行实现,同时要求访问权限修饰符、返回值类型、方法名和参数列表必须完全一致。
接口和继承,Java 只支持单继承,但是接口可以多实现(一个实现类可以同时实现多个接口)
//接口一public interface MyInterface { public int ID = 0; String NAME = "张三"; public void fly();}//接口二public interface MyInterface2 { public void run();}//继承两个接口public class MyInterfaceImpl implements MyInterface,MyInterface2 { @Override public void run() { // TODO Auto-generated method stub System.out.println("实现了跑步的方法"); } @Override public void fly() { // TODO Auto-generated method stub System.out.println("实现了飞行的方法"); }}//测试public class Test { public static void main(String[] args) { MyInterfaceImpl myInterfaceImpl = new MyInterfaceImpl(); myInterfaceImpl.fly(); myInterfaceImpl.run(); }}
六、常用类
1、Object
Object 是 Java 官方提供的类,存放在 java.lang 包中,该类是所有类的直接父类或者间接父类,无论是 Java 提供的类还是开发者自定义的类,都是 Object 的直接子类或间接子类,Java 中的任何一个类都会继承 Object 中的 public 和 protected 方法。
hashCode();getClass();equals(null);clone();toString();notify();notifyAll();wait();wait(1000L);wait(1000L, 100);
Object 类中经常被子类重写的方法:
1、public String toString() 以字符串的形式返回对象的信息
2、public boolean equals(Object obj) 判断两个对象是否相等
3、public native int hashCode() 返回对象的散列码
- toString
Objectpublic String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode());}//重写之后@Overridepublic String toString() { return "People [id=" + id + ", name=" + name + ", score=" + score + "]";}
- equals
Objectpublic boolean equals(Object obj) { return (this == obj);}//重写之后@Overridepublic boolean equals(Object obj) { // TODO Auto-generated method stub People people = (People)obj; if(this.id == people.id && this.name.equals(people.name) && this.score.equals(people.score)) { return true; } return false;}
- hashCode
Objectpublic native int hashCode();//重写之后@Overridepublic int hashCode() { // TODO Auto-generated method stub return (int) (id*name.hashCode()*score);}
2、包装类
- 什么是包装类?
包装类是 Java 提供的一组类,专门用来创建 8 种基本数据类型对应的对象,一共有 8 个包装类,存放在 java.lang 包中,基本数据类型对应的包装类。
包装类的体系结构
Java 官方提供的一组类,这组类的作用是将基本数据类型的数据封装成引用类型。
Byte、Integer、Short、Long、Float、Double、Boolean、Characte
3、装箱和拆箱
装箱和拆箱是包装类的特有名词,装箱是指将基本数据类型转为对应的包装类对象,拆箱就是将包装类对象转为对应的基本数据类型。
装箱与拆箱
装箱是指将基本数据类型转换为包装类对象。
拆箱是指将包装类对象转换为基本数据类型。
3.1、构造函数
1、public Type(type value) 【即原类型】
每个包装类都提供了一个有参构造函数:public Type(type value),用来实例化包装类对象。
byte b = 1;Byte byt = new Byte(b);short s = 2;Short shor = new Short(s);int i = 3;Integer integer = new Integer(i);long l = 4;Long lon = new Long(l);float f = 5.5f;Float flo = new Float(f);double d = 6.6;Double dou = new Double(d);char cha = 'J';Character charac = new Character(cha);boolean bo = true;Boolean bool = new Boolean(bo);System.out.println(byt);System.out.println(shor);System.out.println(integer);System.out.println(lon);System.out.println(flo);System.out.println(dou);System.out.println(charac);System.out.println(bool);
2、public Type(String value)/public Type(char value) 【即字符 / 字符串类型】
每个包装类还有一个重载构造函数:
Character 类的重载构造函数:public Type(char value)
,其他包装类的重载构造函数:public Type(String value)
。
Byte byt = new Byte("1");Short shor = new Short("2");Integer integer = new Integer("3");Long lon = new Long("4");Float flo = new Float("5.5f");Double dou = new Double("6.6");Character charac = new Character('J');Boolean bool = new Boolean("abc");System.out.println(byt);System.out.println(shor);System.out.println(integer);System.out.println(lon);System.out.println(flo);System.out.println(dou);System.out.println(charac);
需要注意的是,Boolean 类的构造函数中,当参数为 “true” 时,Boolean 值为 true,当参数不为 “true”,Boolean 值为 false。
3.2、装箱
1、public Type(type value)
2、public Type(String value)/public Type(char value)
3、valueOf(type value) 静态方法,参数是基本数据类型的数据
每一个包装类都有一个 valueOf(type value) 方法
byte b = 1;Byte byt = Byte.valueOf(b);short s = 2;Short shot = Short.valueOf(s);int i = 3;Integer integer = Integer.valueOf(i);long l = 4L;Long lon = Long.valueOf(l);float f = 5.5f;Float floa = Float.valueOf(f);double d = 6.6;Double doub = Double.valueOf(d);boolean boo = true;Boolean bool = Boolean.valueOf(boo);char ch = 'J';Character cha = Character.valueOf(ch);
其中:
//valueOf(String value)/valueOf(char value) 专门为 Character 转换使用的,//其他的 7 个包装类都可以使用 valueOf(String value)。Byte byt = Byte.valueOf("1");Short sho = Short.valueOf("2");Integer integer = Integer.valueOf("3");Long lon = Long.valueOf("4");Float flo = Float.valueOf("5.5f");Double dou = Double.valueOf("6.6");Boolean boo = Boolean.valueOf("true");Character cha = Character.valueOf('J');
需要注意的是 Boolean.valueOf(String value) 方法中,当 value 为 “true” 时,Boolean 的值为 true,否则,Boolean 的值为 false。
3.3、拆箱
1、*Value()
每个包装类都有一个 *Value() 方法,通过该方法可以将包装类转为基本数据类型。
Byte byt = Byte.valueOf("1");Short sho = Short.valueOf("2");Integer integer = Integer.valueOf("3");Long lon = Long.valueOf("4");Float flo = Float.valueOf("5.5f");Double dou = Double.valueOf("6.6");Boolean boo = Boolean.valueOf("true");Character cha = Character.valueOf('J');byte b = byt.byteValue();short sh = sho.shortValue();int i = integer.intValue();long l = lon.longValue();float f = flo.floatValue();double d = dou.doubleValue();boolean bo = boo.booleanValue();char c = cha.charValue();
2、parse*(String value)
除了 Character
类以外的每一个包装类都有一个静态方法
可以将字符串类型转为基本数据类型。
byte b = Byte.parseByte("1");short s = Short.parseShort("2");int i = Integer.parseInt("3");long l = Long.parseLong("4");float f = Float.parseFloat("5.5");double d = Double.parseDouble("6.6");boolean bo = Boolean.parseBoolean("true");
3、toString(type value)
每个包装类都有该方法,作用是将基本数据类型转为 String 类型。
byte b = 1;String bstr = Byte.toString(b);short s = 2;String sstr = Short.toString(s);String i = Integer.toString(3);long l = 4L;String lstr = Long.toString(l);float f = 5.5f;String fstr = Float.toString(f);double d = 6.6;String dstr = Double.toString(d);boolean bo = true;String bostr = Boolean.toString(bo);String chstr = Character.toString('J');
七、异常
1、基本概念
- 什么是异常?
Java 中的错误大致可以分为两类:
1、一类是编译时错误,一般是指语法错误。
2、另一类是运行时错误。
Java 中有一组专门用来描述各种不同的运行时异常,叫做异常类
,Java 结合异常类提供了处理错误的机制。
具体步骤是当程序出现错误时,会创建一个包含错误信息的异常类的实例化对象,并自动将该对象提交给系统,由系统转交给能够处理异常的代码进行处理。
异常可以分为两类:【Error 和 Exception】:
1、Error 是指系统错误,JVM 生成,我们编写的程序无法处理。
2、Exception 指程序运行期间出现的错误,我们编写的程序可以对其进行处理。
Error 和 Exception 都是 Throwable 的子类,Throwable、Error、Exception 都是存放在 java.lang 包中。
- 异常的使用
异常的使用需要用到两个关键字 try 和 catch,并且这两个关键字需要结合起来使用,用 try 来监听可能会抛出异常的代码,一旦捕获到异常,生成异常对象并交给 catch 来处理,基本语法如下所示。
try{ //可能抛出异常的代码}catch(Exception e){ //处理异常}
package com.southwind.exception;public class Test { public static void main(String[] args) { try { int num = 10/10; }catch (Exception e) { // TODO: handle exception if(e.getMessage().equals("/ by zero")) { System.err.println("分母不能为0"); } } }}
除了 try 和 catch,还可以使用 finally 关键字来处理异常,finally 的作用?
无论程序是否抛出异常,finally 代码块中的代码一定都会执行,finally 一般跟在 catch 代码块的后面,基本语法如下所示。
try{ //可能抛出异常的代码}catch(Exception e){ //处理异常}finally{ //必须执行的代码}
2、异常类
Java 将运行时出现的错误全部封装成类,并且不是一个类,而是一组类。同时这些类之间是有层级关系的,由树状结构一层层向下分级,处在最顶端的类是 Throwable,是所有异常类的根结点。
Throwable 有两个直接子类:
- Error
- VirtualMachineError
- StackOverflowError
- OutOfMemoryError
- AWTError
- IOError
- VirtualMachineError
- Exception。
- IOException
- FileLockInterruptionException
- FileNotFoundException
- FilerException
- RuntimeException
- ArithmeticException
- ClassNotFoundException
- IllegalArggumentException
- ArrayIndexOutOfBoundsException
- NullPointerException
- NoSuchMethodException
- NumberFormatException
3、throw 和 throws
throw 和 throws 是 Java 在处理异常时使用的两个关键字,都可以用来抛出异常,但是使用的方式和表示的含义完全不同。
Java 中抛出异常有 3 种方式:
- try-catch
- 使用 throw 是开发者主动抛出异常,即读到 throw 代码就一定抛出异常,基本语法:throw new Exception(),是一种基于代码的逻辑而主动抛出异常的方式。
public class Test { public static void main(String[] args) { int[] array = {1,2,3}; test(array,2); } public static void test(int[] array,int index) { if(index >= 3 || index < 0) { try { throw new Exception(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }else { System.out.println(array[index]); } }}
- try-catch 和 throw 都是作用于具体的逻辑代码,throws 是作用于方法的,用来描述方法可能会抛出的异常。
如果方法 throws 的是 RuntimeException 异常或者其子类,外部调用时可以不处理,JVM 会处理。
如果方法 throws 的是 Exception 异常或者其子类,外部调用时必须处理,否则报错。
public class Test { public static void main(String[] args) throws Exception { test("123"); } public static void test(String str) throws Exception { int num = Integer.parseInt(str); }}
4、异常捕获
- 自动捕获 try-cath
- throw 主动抛出异常
- throws 修饰可能抛出异常的方法
5、自定义异常
除了使用 Java 提供的异常外,也可以根据需求来自定义异常。
package com.southwind.exception;public class MyNumberException extends RuntimeException { public MyNumberException(String error) { super(error); }}
package com.southwind.exception;public class Test { public static void main(String[] args){ Test test = new Test(); System.out.println(test.add("a")); } public int add(Object object){ if(object instanceof Integer) { int num = (int)object; return ++num; }else { String error = "传入的参数不是整数类型"; MyNumberException myNumberException = new MyNumberException(error); throw myNumberException; } }}
6、综合练习
封装、继承、多态、抽象、接口、异常完成一个汽车查询系统。
需求描述:共有 3 种类型的汽车:小轿车、大巴车、卡车,其中小轿车的座位数是 4 座,大巴车座位数是 53 座,卡车座位数是 2 座,要求使用封装、继承、抽象来完成车辆的定义。
可以对车辆信息进行修改,卡车可以运货但是载重量不能超过 12 吨,使用自定义异常来处理错误,小轿车和大巴车没有此功能,要求使用接口来实现。
Car
package com.southwind.test;public abstract class Car { private String name; private String color; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public Car(String name, String color) { super(); this.name = name; this.color = color; } public abstract String seatNum();}
Sedan
package com.southwind.test;public class Sedan extends Car { public Sedan(String name, String color) { super(name, color); } @Override public String seatNum() { // TODO Auto-generated method stub return "4座"; }}
Bus
package com.southwind.test;public class Bus extends Car { public Bus(String name, String color) { super(name, color); // TODO Auto-generated constructor stub } @Override public String seatNum() { // TODO Auto-generated method stub return "53座"; }}
Truck
package com.southwind.test;public class Truck extends Car implements Container { private int weight; public Truck(String name, String color,int weight) { super(name, color); this.weight = weight; // TODO Auto-generated constructor stub } @Override public String seatNum() { // TODO Auto-generated method stub return "2座"; } @Override public int getweight() { // TODO Auto-generated method stub return this.weight; }}
Container
package com.southwind.test;public interface Container { public int getweight();}
CarException
package com.southwind.test;public class CarException extends Exception { public CarException(String error) { super(error); }}
Test
package com.southwind.test;import java.util.Scanner;public class Test { private static Scanner scanner; private static Sedan sedan; private static Bus bus; private static Truck truck; private static Car[] cars; static { scanner = new Scanner(System.in); sedan = new Sedan("小轿车","黑色"); bus = new Bus("大巴车","绿色"); truck = new Truck("卡车","蓝色",2); cars = new Car[3]; cars[0] = sedan; cars[1] = bus; cars[2] = truck; } public void showCars() { System.out.println("欢迎使用本汽车管理系统"); System.out.println("车辆名称\t\t车辆颜色\t\t座位数\t\t载重量"); for(Car car:cars) { if(car instanceof Truck) { Truck truck = (Truck)car; System.out.println(car.getName()+"\t\t"+car.getColor()+"\t\t"+car.seatNum()+"\t\t"+truck.getweight()); }else { System.out.println(car.getName()+"\t\t"+car.getColor()+"\t\t"+car.seatNum()+"\t\t不能拉货"); } } System.out.println("1.小轿车\t2.大巴车\t3.卡车"); System.out.print("请选择要修改的车辆:"); int num = scanner.nextInt(); switch(num) { case 1: update("sedan"); break; case 2: update("bus"); break; case 3: update("truck"); break; default: System.out.println("车辆不存在!"); break; } } public void update(String type) { String name = null; String color = null; if(type.equals("sedan")) { System.out.print("输入车辆名称"); name = scanner.next(); System.out.print("输入车辆颜色"); color = scanner.next(); Sedan sedan = new Sedan(name,color); cars[0] = sedan; } if(type.equals("bus")) { System.out.print("输入车辆名称"); name = scanner.next(); System.out.print("输入车辆颜色"); color = scanner.next(); Bus bus = new Bus(name,color); cars[1] = bus; } if(type.equals("truck")) { System.out.print("输入车辆名称"); name = scanner.next(); System.out.print("输入车辆颜色"); color = scanner.next(); System.out.print("输入载重量"); int weight = scanner.nextInt(); if(weight > 12) { CarException carException = new CarException("卡车的载重量不能超过12吨"); try { throw carException; } catch (CarException e) { // TODO Auto-generated catch block e.printStackTrace(); return; } } Truck truck = new Truck(name,color,weight); cars[2] = truck; } showCars(); } public static void main(String[] args) { Test test = new Test(); test.showCars(); }}
讲解了面向对象的高级部分,包括 Object 类、包装类、接口和异常。其中 Object 类是所有 Java 类的父类,定义了 Java 体系的基础资料,通过继承传递给 Java 的每一个类,通过方法重写和多态让整个 Java 体系具有很强的灵活性。
包装类是 Java 为基本数据类型提供封装的一组类,通过包装类我们可以将基本数据类型转为对象,这一点在面向对象编程中很重要。
接口是抽象类的扩展,是 Java 中实现多态的重要方式,可以降低程序的耦合性,让程序变得更加灵活多变。接口就相当于零件,我们可以自由地将这些零件进行组装、整合。
异常是 Java 中处理错误的一种机制,同样是基于面向对象的思想,将错误抽象成对象然后进行处理,这里需要关注的是对异常相关的几个关键字的使用,try、catch、finally、throw、throws。
八、多线程
1、多线程
多线程是提升程序性能非常重要的一种方式,必须掌握的技术。
使用多线程可以让程序充分利用 CPU 资源。
优点:
- 系统资源得到更合理的利用。
- 程序设计更加简洁。
- 程序响应更快,运行效率更高。
缺点:
- 需要更多的内存空间来支持多线程。
- 多线程并发访问的情况可能会影响数据的准确性。
- 数据被多线程共享,可能会出现死锁的情况。
2、进程和线程
什么是进程:进程就是计算机正在运行的一个独立的应用程序。
进程是一个动态的概念,当我们启动某个应用的时候,进程就产生了,当我们关闭该应用的时候,进程就结束了,进程的生命周期就是我们在使用该软件的整个过程。
什么是线程?
线程是组成进程的基本单位,可以完成特定的功能,一个进程是由一个或多个线程组成的。
应用程序是静态的,进程和线程是动态的,有创建有销毁,存在是暂时的,不是永久的。
进程和线程的区别:
进程在运行时拥有独立的内存空间,即每个进程所占用的内存空间都是独立的,互不干扰。线程是共享内存空间的,但是每个线程的执行都是相互独立的,单独的线程是无法执行的,由进程来控制多个线程的执行。
3、多线程
多线程是指在一个进程中,多个线程同时执行,这里说的同时执行并不是真正意义的同时执行。
系统会为每个线程分配 CPU 资源,在某个具体的时间段内 CPU 资源会被一个线程占用,在不同的时间段内由不同的线程来占用 CPU 资源,所以多个线程还是在交替执行,只不过因为 CPU 运行速度太快,我们感觉是在同时执行。
整个程序如果是一条回路,说明程序只有一个线程。
程序有两条回路,同时向下执行,这种情况就是多线程,两个线程同时在执行。
4、Java 中线程的使用
Java 中使用线程有两种方式:
- 继承 Thread 类
- 实现 Runnable 接口
Java 写程序三部分组成:
1、JDK 系统类库
JRE:Java Runtime Enviroment(Java 运行环境),仅供运行程序的。
JDK:Java Development Kit(Java 开发工具包),如果需要进行程序开发,必须安装 JDK。
String、Scanner、包装类。。。
java.lang.Thread
javax.servlet.Servlet
2、第三方类库
非 Java 官方的组织提供的一些成熟好用的工具,C3P0 数据库连接池、Spring 框架、DBUtils、Dom4J…
github:全球最大的同性交友网站
3、开发者自定义的代码
根据具体的业务需求编写的业务代码。
5、Java 中线程的使用
- 继承 Thread 类
1、创建自定义类并继承 Thread 类。
2、重写 Thread 类中的 run 方法,并编写该线程的业务逻辑代码。
public class MyThread extends Thread { @Override public void run() { // TODO Auto-generated method stub //定义业务逻辑 for(int i = 0;i<10;i++) { System.out.println("-------------MyThread"); } }}
3、使用。
package com.southwind.test;public class Test { public static void main(String[] args) { //开启两个子线程 MyThread thread1 = new MyThread(); MyThread2 thread2 = new MyThread2(); thread1.start(); thread2.start(); }}
注意:不能通过 run 方法来调用线程的任务,因为 run 方法调用相当于普通对象的执行,并不会去抢占 CPU 资源。
只有通过start
方法才能开启线程,进而去抢占 CPU 资源,当某个线程抢占到 CPU 资源后,会自动调用 run 方法。
- 实现 Runnable 接口
1、创建自定义类并实现 Runnable 接口。
2、实现 run 方法,编写该线程的业务逻辑代码。
public class MyRunnable implements Runnable { @Override public void run() { // TODO Auto-generated method stub for(int i=0;i<1000;i++) { System.out.println("========MyRunnable======="); } }}
3、使用。
MyRunnable runnable = new MyRunnable();Thread thread = new Thread(runnable);thread.start();MyRunnable2 runnable2 = new MyRunnable2();Thread thread2 = new Thread(runnable2);thread2.start();
线程和任务:
线程是去抢占 CPU 资源的,任务是具体执行业务逻辑的,线程内部会包含一个任务,线程启动 (start),当抢占到资源之后,任务就开始执行 (run)。
两种方式的区别:
1、MyThread,继承 Thread 类的方式,直接在类中重写 run 方法,使用的时候,直接实例化 MyThread,start 即可,因为 Thread 内部存在 Runnable。
2、MyRunnbale,实现 Runnable 接口的方法,在实现类中重写 run 方法,使用的时候,需要先创建 Thread 对象,并将 MyRunnable 注入到 Thread 中,Thread.start。
实际开发中推荐使用第二种方式。
在线画图软件:https://www.processon.com/diagrams
6、线程的状态
线程共有 5 种状态,在特定的情况下,线程可以在不同的状态之间切换,5 种状态如下所示。
- 创建状态:实例化一个新的线程对象,还未启动。
- 就绪状态:创建好的线程对象调用 start 方法完成启动,进入线程池等待抢占 CPU 资源。
- 运行状态:线程对象获取了 CPU 资源,在一定的时间内执行任务。
- 阻塞状态:正在运行的线程暂停执行任务,释放所占用的 CPU 资源,并在解除阻塞状态之后也不能直接回到运行状态,而是重新回到就绪状态,等待获取 CPU 资源。
- 终止状态:线程运行完毕或因为异常导致该线程终止运行。
线程状态之间的转换图。
7、Java 多线程的实现
- 继承 Thread
- 实现 Runnable
8、线程调度
8.1、线程休眠
让当前线程暂停执行,从运行状态进入阻塞状态,将 CPU 资源让给其他线程的调度方式,通过 sleep() 来实现。
sleep(long millis),调用时需要传入休眠时间,单位为豪秒。
public class MyThread extends Thread{ @Override public void run() { // TODO Auto-generated method stub for(int i=0;i<10;i++) { if(i == 5) { try { sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(i+"---------MyThread"); } }}
也可以在类的外部调用 sleep 方法。
MyThread2 thread = new MyThread2();try { thread.sleep(5000);} catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace();}thread.start();
在外部调用需要注意,休眠一定要放在启动之前。???
如何让主线程休眠?直接通过静态方式调用 sleep 方法。
public class Test2 { public static void main(String[] args) { for(int i=0;i<10;i++) { if(i == 5) { try { Thread.sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(i+"+++++Test2+++++"); } }}
public static native void sleep(long millis) throws InterruptedException;
sleep 是静态本地方法,可以通过类调用,也可以通过对象调用,方法定义抛出 InterruptedException,InterruptedException 继承 Exception,外部调用时必须手动处理异常。
8.2、线程合并
合并是指将指定的某个线程加入到当前线程中,合并为一个线程,由两个线程交替执行变成一个线程中的两个自线程顺序执行。
通过调用 join 方法来实现合并,具体如何合并?
线程甲和线程乙,线程甲执行到某个时间点的时候调用线程乙的 join 方法,则表示从当前时间点开始 CPU 资源被线程乙独占,线程甲进入阻塞状态,直到线程乙执行完毕,线程甲进入就绪状态,等待获取 CPU 资源进入运行状态。
join 方法重载,join() 表示乙线程执行完毕之后才能执行其他线程,join(long millis) 表示乙线程执行 millis 毫秒之后,无论是否执行完毕,其他线程都可以和它争夺 CPU 资源。
public class JoinRunnable implements Runnable { @Override public void run() { // TODO Auto-generated method stub for(int i=0;i<200;i++) { System.out.println(i+"------JoinRunnable"); } }}
public class JoinTest { public static void main(String[] args) { /** * 两个线程,主线程、join线程 * 主线程的逻辑:当i==10,join线程合并到主线程中 */ JoinRunnable joinRunnable = new JoinRunnable(); Thread thread = new Thread(joinRunnable); thread.start(); for(int i=0;i<100;i++) { if(i == 10) { try { thread.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(i+"main+++++++++"); } }}
public class JoinRunnable2 implements Runnable { @Override public void run() { // TODO Auto-generated method stub for(int i=0;i<20;i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(i+"--------JoinRunnable"); } }}
public class Test2 { public static void main(String[] args) { for(int i=0;i<10;i++) { if(i == 5) { try { Thread.sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(i+"+++++Test2+++++"); } }}
8.3、线程礼让
线程礼让是指在某个特定的时间点,让线程暂停抢占 CPU 资源的行为,运行状态 / 就绪状态—》阻塞状态,将 CPU 资源让给其他线程来使用。
假如线程甲和线程乙在交替执行,某个时间点线程甲做出了礼让,所以在这个时间节点线程乙拥有了 CPU 资源,执行业务逻辑,但不代表线程甲一直暂停执行。
线程甲只是在特定的时间节点礼让,过了时间节点,线程甲再次进入就绪状态,和线程乙争夺 CPU 资源。
通过 yield 方法实现。
public class YieldThread1 extends Thread { @Override public void run() { // TODO Auto-generated method stub for(int i = 0; i < 10;i++) { if(i == 5) { yield(); } System.out.println(Thread.currentThread().getName()+"-----"+i); } }}
public class YieldThread2 extends Thread { @Override public void run() { // TODO Auto-generated method stub for(int i=0;i<10;i++) { System.out.println(Thread.currentThread().getName()+"======"+i); } }}
package com.southwind.yield;public class Test { public static void main(String[] args) { YieldThread1 thread = new YieldThread1(); thread.setName("线程1"); YieldThread2 thread2 = new YieldThread2(); thread2.setName("线程2"); thread.start(); thread2.start(); }}
8.4、线程中断
本文由博客一文多发平台 OpenWrite 发布!