目录
4.2.1 if if-else if-else if - else
Java基础知识点总结归纳,超级全面!(2021版)_java复习基础-CSDN博客
1、JVM、JRE和JDK
2、IDEA使用
(1)IntelliJ IDEA中右键新建时,选项没有Java class的解决方法和具体解释:
IntelliJ IDEA认为这个文件不是 项目/模板 ,虽然项目或者模板其实也是一个文件夹,但是你要让IntelliJ IDEA知道这是一个可以创建Java class类的文件(项目 / 模板):请File—>Project Structure—>Modules。选择刚才的包,单击Sources。添加到源中
(2)运行流程:我用的JDK11
3、数据类型 运算符
3.1 基本数据类型
数值类型
浮点类型:默认小数点是双精度double,所以float=1.0会报错,需要指定是单精度float f=1.0f或F
字符类型:使用符号标识文字char c = '@';
布尔类型:true false
基本数据类型之间的转换:
byte不能转char,因为char无负数
3.2 引用数据类型
即可以被引用的数据类型
String s = “abc”; 但要注意int i = 10不称为引用数据类型,应为后者10直接写在了i的地址中,而s中写的是abc的地址,算是一种引用。
3.3 运算法
4、流程控制
4.1 顺序执行
代码出现的先后顺序和语法的先后顺序
4.2 分支执行
4.2.1 if if-else if-else if - else
4.2.2 switch
这是一种等值判断
i = 10,匹配到第一个,接下来就是从输出分支1开始顺序执行,输出分支2 分支3;若i=20,输出分支2 分支3;因为没有写break;都不匹配直接全跳过,可以在最后加上default。
4.3 重复执行
4.3.1 while
while(true){
循环代码;
}
先判断再执行,直到()中为false才结束
4.3.2 do...while
do {
循环代码;
}while(true)
先执行再判断,直到()中为false才结束
4.3.3 for
还有一种 for 的写法:
4.3.4 break continue
break:跳出整个循环
continue:跳过本循环
break 跳出总上一层循环,不再执行循环(结束当前的循环体)
continue 跳出本次循环,继续执行下次循环(结束正在执行的循环 进入下一个循环条件)
return 程序返回,不再执行下面的代码(结束当前的方法 直接返回)
5、面向对象
5.1 类和对象
等号左边声明了一个变量,右边创建了一个对象,然后将对象赋值给了变量,可用下图表示:
5.2 属性
5.3 方法
5.3.1 方法中的参数
1、不传参数
2、基本的传参,考虑传参个数,顺序,类型
3、存在可变参数(即,有好几个参数但数目不定,类型相同)
5.3.2 变量作用域
输出10,因为传的是形参,test()执行结束后,该函数内部的i就释放了,对main中的i没有影响!
5.3.3 静态static
- 被static修饰的变量或者方法是独立于该类的任何对象,也就是说,这些变量和方法不属于任何一个实例对象,而是被类的实例对象所共享。
- 在该类被第一次加载的时候,就会去加载被static修饰的部分,而且变量只在类第一次使用时加载并进行初始化,注意这是第一次用就要初始化,后面根据需要是可以再次赋值的。
- static变量值在类加载的时候分配空间,以后创建类对象的时候不会重新分配。赋值的话,是可以任意赋值的!(这句话在单例模式中再一次深刻体现出来!)
- 被static修饰的变量或者方法是优先于对象存在的,也就是说当一个类加载完毕之后,即便没有创建对象,也可以去访问。
1、使用方法
2、静态 和 非静态 的关系
(1)先有类 再有对象!!!
使用时,对象也能访问静态属性和方法——原因先有类 再有对象!!!
class中定义方法时:
非静态方法可以调用静态属性和方法——原因非静态的方法时面向对象的,使用时一定是对象打点访问,而有对象就是有类了。
静态方法可能调用非静态属性或方法吗?—— 不能。因为使用类调用时,未必有对象。
(2)代码块
类被第一次加载的时候(创建对象就会加载类,或是直接使用类名调用静态函数或属性),就会去加载被static修饰的部分(注意只有第一次加载类才会调用),但“代码块执行1”不会输出,因为不是静态的。“代码块执行1”在每次创建对象时,会自动调用。
5.4 包
5.4.1 package
5.4.2 import
import是用来导入其他包中的类的
5.5 构造函数
在使用new创建对象时,也就是在构建对象,需要调用一个构造函数,所以上图中划横线的()就是在调用构造函数,是一个无参数的构造函数。
若有参数,在new时需要传参;
!代码块,是在构造对象之前执行的
5.6 继承 Extends
5.6.1 基本语法
1、当父类和子类中存在名字一样的属性时:super & this
默认,this省略。
当父类和子类中存在一样方法时,方法重写。
2、父类对象是在每个子类对象创造前创建
所以,创建对个子类对象时,每个对象都会链接到一个父类对象上(只是这样说,实际是内存分配的问题)
运行结果:其实是因为在调用子类的构造函数Child2()时,JVM默认执行的第一条指令不是输出child这一行,而是隐藏了的 super(); 这一行的含义是调用父类的构造函数。
所以如果父类自己定义了构造函数,且有参数时,需要在子类的构造函数中显式的使用super方法构建父类对象,传入合适的参数。
5.6.2
5.7 多态
三种Person对象的创建都可以,因为子类已经继承了父类。通俗说就是把子类当做父类用
但是使用p1或p2来调用子类中特有的函数或属性就不对了,因为这两个对象是Person,不具备子类特有的内容
所以 —— 多态语法其实就是对对象的使用场景进行了约束!
—— 一个对象可以使用的功能取决于引用变量的类型!
5.8 方法重载
1、构造函数也存在方法的重载!
2、如果想要在一个构造函数中调用其他构造函数,使用关键字 this:
3、基本数据类型在匹配方法时,可以在数值不变的情况下,扩大数据精度
此时输出:bbb
如果把方法1删了,那么输出sss(找不到匹配的类型,逐步扩大精度,byte是4bit,short是16bit,int是32bit);再把方法2也删了,输出iii(byte不能转char)。
注意这是对于基本数据类型而言,如何是类对象找不到合适的,会向前找父类。ex没截图。P54中
5.9 方法重写
父类和子类有一样的方法(注,方法一样说明参数也一样哦!要不然不是一个方法),怎么办?
当然也能像属性一样使用super:
方法重写的要求:子类父类方法相同,名一样,返回值类型一样,参数列表一样。
所以构造方法可以重写吗?—— 不能,名字就不一样。
易错点:
创建对象:
CCC ccc = new CCC(); System.out.println(ccc.sum()); 输出:20
DDD ddd = new DDD(); System.out.println(ddd.sum()); 输出:40
CCC ccc = new DDD(); System.out.println(ccc.sum()); 输出:40
根据上面两句高亮的话,ccc是CCC类型,所以可以使用sum,但是具体如何用,看ccc是什么对象,是DDD对象,所以实际运行的是DDD中的sum。
(方法:编译看左,执行看右)
如果删去DDD中的sum呢?输出:20
原因——上面图中没有高亮的两句话。ccc在使用属性变量时能使用什么属性仍然看类型,因为是CCC类型,所以可以使用变量i,但如何使用和方法的规则不同了,变量的使用不看对象,在哪里声明在哪里使用!即因为调用的是父类的sum,而父类的sum中的 i 是在父类中声明的,所以i=10,结果为20。同样在没删子类sum时,执行的是子类的sum,子类sum中的 i 是在子类中有声明的,默认是 this.i 哦(若子类没有声明,继承父类的i=10),所以i=20,结果为40.
如果父类子类改为:
CCC ccc = new DDD(); System.out.println(ccc.sum()); 输出:30
因为——方法直接或间接调用都是:能不能用看类型,怎么用看对象!
所以sum执行的是CCC中的,但是因为ccc是DDD对象,getI()仍是先从子类找起,子类有,所以执行的是子类的getI(),此时getI中的 i 也是this.i,即子类声明的 i=20。
5.10 递归
求等差数列:
5.11 访问权限
函数内定义的变量只在该函数内{ }起作用,在函数内找变量,先看自己函数内有没有,包括参数。
对于class的属性:
4中权限依次增大
public
所以main方法需要是public,又因为main方法不需要创建类对象就能调用执行,所以必须加上static,如果取消,可以发现main无法直接执行了,需要创建对象来调用。
default
默认,当前包下,这与父包子包无关,必须是当前路径
protected
子类可以访问,即使是不同包的子类也能。
指的是子类class内部可用,但若是在又在另一个class中创建了该子类对象,再用对象访问protected,不可行:下图自己悟吧,所有类默认继承Object类。
public
5.12 内部类
5.13 单例模式
在上面1和2的情况下,会考虑将构造函数设置为private,禁止随意创建对象。
所以又写了一个public的static的创建对象函数,可以在main中使用类名直接调用来创建对象,
但如果是这样的:直接返回一个new的对象,那么在main中的多个instance仍指向不同的对象,没有达到减少资源消耗等目标,所以又加了一个私有的静态的属性来判断目前是否已经创建了一个对象,如果是则直接返回已创建好的。
这就是static的性质,static变量值在类加载的时候分配空间,以后创建类对象的时候不会重新分配,但可以重新赋值。所以内存中只有一个User19 user19对象。
5.14 final
final修饰的属性,必须初始化,可以直接public final String name = “zy”; 如果这里没有赋值,必须在构造函数中赋值。构造函数中赋值:写成name = name就不对了,这里的两个name都会被认为是构造函数的参数name,不是类属性name,所以要使用this.name表明是这个类的属性。
因为构造函数本身就不会被子类重写,所以不能加final。
5.15 抽象-Abstract
当类或是方法的内容不能够很确定时,定义“抽象类” “抽象方法”。
子类继承,重写。然后才能用子类创建对象。
注:abstract 和 final 两个关键字不能一起使用。
5.16 接口-interface
一个例子,
创建一个接口:
定义了一个USB接口,它又需要一个USB提供者和接受者。
类实现接口,可实现多个,这里一个类对应实现了一个:
实现:电脑作为提供能源的一方,实现USBSupply接口中的抽象函数,同时电脑有两个usb口,可以接两个USB设备,这两个设备是USB能源接收方,实现了接收方的接口,可以调用powerReceive函数。
台灯作为USB另一端,接收能源,所以要实现对应的抽象函数。
创建对象:
创建一个提供电源的电脑对象c,两个台灯,c.usb1 = light 将该台灯对象插到了c的usb1接口上。
输出:
5.17 枚举-enum
1、简单来说:
2、复杂一点:
如果有语法,对象和语法分号隔开,对象之间都是逗号;
BEIJING 和SHANGHAI 都是City的对象,而语法中含有参数时,这个对象就应该给参数赋值;
枚举类型,在外面不能创建对象,必须写在enum里面,且写在最前面;
这样的功能也可以使用class来实现:
此时该类也不能在main中new了。
5.18 匿名类
对于抽象的类,可以不使用子类实现,直接使用匿名类创建对象,接口也可以。
例子:
1、抽象类
使用了子类实现抽象类,main中创建子类对象来访问类中内容,但是若再来一个Person24,又要用一个子类来实现,所以很麻烦,不如直接使用匿名类来实现抽象类,并创建对象。
2、接口:
使用输出;
可也匿名使用接口:
5.19 bean规范
5.20 作用域
就近哦
6、常用类和对象
6.1 object
所以类默认继承object
6.2 数组
6.2.1 一维数组
声明方式:类型[ ] 变量;
创建: new 类型[容量]; int[ ] a = new int[3];
赋值:为数组中每个元素赋值
创建好之后,系统会默认的根据类型为数组赋值
也可以在创建数组的同时为数组赋值:int[ ] a = { 1,2,3 };
这里的 for 循环也可使用 for 的另一种方式:
for (String name : names) {
System.out.println(name);
}
创建类对象也可以创建对象数组:
6.2.2 二维数组
6.2.3 冒泡排序
1、
6.2.4 选择排序
6.2.5 二分查找法
6.3 字符串
6.3.1 字符串创建
字节Byte,字符、字符串关系:
a、A、中、+、*、の......均表示一个字符;
一般 utf-8 编码下,一个汉字 字符 占用 3 个 字节;
一般 gbk 编码下,一个汉字 字符 占用 2 个 字节;
字符串创建:
- 使用new是创建了一个新的对象,并可以为其赋值为zhangsan,但是JVM为了方便,可以简化new,直接在等号右边不写new,直接赋值zhangsan,这两种方法都是创建了一个String类型的对象,并赋值张三。但有一点不同的是,如果name4,name2,name3都是使用new来创建并初始化的,那么他们里面包含的内容,即zhangsan这个字符串,在内存中的地址是不一样的,然后使用图示方法创建name4,name2,name3,虽然它们三个是同一类型的三个不同对象,但是这三个对象指向的三个字符串zhangsan其实在内存中是同一块区域,可使用hashCode()来查看内存地址。
- char数组就是字符数组,每个元素是一个字符,而String就是一个字符串,所以char[ ]可以直接赋值给String。但是byte数组不能,如果非要写要指定编码方式(此时要继承一下对应的类)。
输出:
- 转义字符
输出:
6.3.2 拼接
使用+和concat()函数,都是拼接,产生一个新的字符串。
+是一个二元运算符,若这两个参加运算的变量中存在String类型,则另一个类型会转为String,下图中的s4:先算的是1+2,然后结果在和"abc"相加,所以最终结果是3abc。
s.hashCode():返回对象的内存地址,此时s和s1的内存地址是一样的,原因上面解释过了。
结果:
6.3.3 比较
输出:false true 32 0
最后输出的32和0其实是这两个String编码成字节对应的数字的差值:
输出差值刚好32
6.3.4 截取
分段:split(),返回一个字符串数组
输出
这张图,是根据空格分解,空格前一部分,后面一部分
去字符串首尾空格:
6.3.5 替换
s.replace("aaa","b"):将一个字符串s中的所有aaa都使用b来替换," "中的内容被看做一个整体;
replaceAll:可以指定多个被替换的字符串部分,所以输出:Hello Java Java
6.3.6 大小写转换
6.3.7 查找
最后一个函数判断字符串是否为空,注意空格不算是空的,虽然看不到,但非空。
6.3.8 StringBuilder
使用+和concat()函数,都是拼接,产生一个新的字符串。这样上面代码就很麻烦,中间过程创建了太多多余的字符串。
这样就好了!
StringBuilder 是一个类,需要创建对象;s.append(i)将i加入到了s中,s相当于是一个字符数组,这个函数不断将i加到这个数组中,所以很方便,节约内存,然后使用toString函数将这个数组转为字符串即可。
StringBuilder 提供的其他函数:
输出:
6.4 包装类
为基本数据类型创建了对应的包装类:
基本数据类型和包装类可以相互转换,同时JVM提供了简便的转换方法:
6.5 日期类 Date
system中有一个返回时间的函数,返回是ms单位
Date类 AimpleDateFormat类
输出:
字符串和Data互相转换:
6.6 日历类 Calendar
6.7 工具类
main:
输出:
6.8 比较
= 和 equals 有区别
对于对象而言,equals来自于父类object,其实这个函数中直接使用了=来判断,所以对象的比较中=和equals一样,但可以重写equals。
包装类也一样,但注意包装类的范围问题!超过返回会new一个新对象,所以用=会False。
7、异常
7.1 介绍
7.2 基础语法
try:
ex:
会报错,没有打印出j,因为错在第二行,直接终端运行。输出:
使用try:
即使有错也不会终端运行了,输出:
因为在try中没有处理问题,所以会抛出错误但没处理,可以在try中直接给出解决方法。
7.3 异常
1、除数为0
2、指针为空
如果name是成员属性,会出错,输出:对象为空,需要分析数据为空的原因
如果是static静态属性,不会报错,因为没对象也能访问。
3、索引越界4、
注意最后一行,截取字符串时,如果传入的参数大于字符串长度才会报错,如4会报错,3不会。
4、格式化异常 类型转换异常
有一个可以判断类是否一致的方法:
7.4 转换异常
使用throws告诉使用者这个可能抛出一个异常,使用者可以使用try处理,也可以不处理,谁调用的丢给谁,所以main后面也加上了throws来处理异常,其实不加也没事,还能编译,因为这是一个运行期异常,运行时会直接报错,然后根据错误改代码就行。但若是希望不仅仅抛出异常还能告诉用户要如何处理或是告诉用户一些事情,那就得保证有错程序还能继续,所以要使用try,并且可以重写应该抛出的异常内容:
这里重写了抛出的内容,在catch到这个除数为0的异常时,会throw一个新的异常:exception,而不再是除为0的异常。
使用这个函数时,会有点问题了:
有编译错误,因为系统此时知道了他可能出错,需要有办法处理:使用try,或丢给main。
7.5 自定义异常
首先:自定义一个登陆异常,又分好几种情况,在这里是账号异常和密码异常,所以又定义了两个子类。super()是显式调用父类的构造函数,一般有参数时需要显式调用。登录异常不需要编译时提示,是运行时检查到有问题,所以是运行时异常,因而继承RuntimeException类
主函数:
输出:
因为main中没处理,所以中断,加上处理:
输出:
!登录异常不需要编译时提示,是运行时检查到有问题,所以是运行时异常,因而继承RuntimeException类。如果继承Exception异常,login函数会报错,因为不知道throw什么异常,所以需要继承对应的类:(这里不是太明白)
也可以直接继承LoginException
8、集合
8.1 介绍
常见的接口和类:
8.2 ArrayList
List — — 说明这是针对Collection接口的,上图有解释啦!
Array — — 数组,说明数据以数组的形式保存
8.2.1 基本操作
1、创建集合对象:
2、增加数据 add(),可以有两个参数,指定位置添加; 打印集合对象:直接print(对象名),返回一个数组;
- 添加数据时,集合为空,自动创建长为10的数组;但若是集合不空,如已指定集合中的数组长为3,此时若是使用add添加了第四个数据,那么系统会自动为集合“扩容”,旧的就不再使用了(这一点和数组类型不同,数组定了就是定了。)
注:在数组中,不是将zhangsan lisi等数据直接放在了数组的这个格子中,而是引用了数据地址。
3、访问集合中的数据
4、修改、删除数据
8.2.2 常用方法
其中最后一个clone复制一个新的集合,生成了通用的对象object,需要创建一个ArrayList对象接受一下它,并需要强制转换。
8.3 LinkedList
linked + list
8.3.1 基本操作
8.3.2 常用方法
add:
remove:
ex:
其他:
其中push是将数据加在了第一个,pop弹出也是第一个。
8.4 泛型
8.4.1 介绍
集合是一个数据的容器,提供了很多方法,但是没有对存储的数据的类型进行一个限制,所以集合中的数据可能什么类型都有,那么在获取数据get()时,只能返回一个大范围的object类型,若想要操作获取到的数据,即如果获取的数据是一个person类,想要调用类中的方法,那么不能直接使用返回的object类,因为此时是一种多态现象,并且类型名限制了这个对象可使用给的方法的范围,所以要将它强制转换为person类型对象:
为了确保正确,还要判断一下获取到的数据是一个什么类型的对象:
很麻烦,所以能不能限制一下,从语法上约束集合中只能放指定类型的数据——泛型<>:
这样这个集合就只能存指定类型数据,get时返回的类型也不是object了而是person:
8.4.2 基本使用
1、类型 vs 泛型
定义一个类,若不知道属性的类型,可使用泛型方法:
创建对象时,指定类型:
此时因为创建时没有指定,也就是什么类型都OK。
若指定,必须遵守。
所以有时也把泛型成为“类型参数”。
- 下图报错了,user7是object子类啊,为啥不能传给object ?
记住就行:泛型没有多态。
8.5 比较器
1、排序:
因为要给sort传一个比较器接口Comparator对象所以实现一下接口(这里o1 o2不懂):
8.6 ArrayList vs LinkedList
删除数据时:如果都是顺序检索,找到再删,则两个一样速度,但是ArrayList可以使用二分查找。
添加第一条数据时,ArrayList需要先创建一片数组空间。
8.7 HashSet
8.7.1 介绍
set:存数据无序,对比list(有序)
hash + set :以hash决定插入到哪一位
冲突:
1、有两个A要存,会覆盖上一个A,所以set中可以不断的加入重复数据,但不会存重复数据
2、两个不同数据但是求出的hash算法值一样,链接方法存放:
所以底层是数组+链表
无修改 查询函数,需要遍历查询:
输出:
8.7.2 常用方法
输出
8.7.3 重复数据
创建一个class:
输出:
这里虽然user1和user2内部数据一样,但其实不同,内存地址不同,所以set中三个数据。
那么如何改动一下呢?内部数据一样就让set认为是一样的,只保存一个。重写下面两个函数:
原本:
改hashcode,保证虽然内存不同,但会被分到一块地址,使其冲突,然后判断是两个一样的数据冲突(丢掉)还是不一样的冲突(链接),原本的equals会返回false,也重写,按照我们的要求,id和name一样就算一样。
改后:
这样再运行上面main,set中只有两个数据。
8.8 Queue
1、加数据
- 加入数据,使用add时:溢出会报错
- 使用put()时:此时可能抛出异常,所以main需要throws一下:
此时运行不报错,但堵塞了,满了,第四个人挂不了号;
- 不报错也不堵塞,offer()
输出的最后三行为:
2、取数据
poll( ):一个个取(从头开始取),没了还取,返回null;
take( ):一个个取(从头开始取),没了还取,堵塞,卡住
8.9 HashMap
hash + map(键值对)
8.9.1 基本语法
一个原理:放的时候是根据key来判断放在哪里,若遇到:
相当于原本zhangsan=1.改为了zhangsan=4,可看做是一个修改操作,可以返回被修改的值,如果是一个新的数据放进去,返回的是0.
8.9.2 常用方法
1、
putifabsent:这个key存在了就不放了
replace:真正的修改函数,返回被修改值,若修改一个不存在key的值,返回null。
2、获取键值对:
返回set,一个集合,但没有体现出来是键值对。因为不确定键和值的类型。
如果约束了类型:
获取时得到的数据类型就明显了,也能体现是键值对:
3、删除
4、key和value分开操作:
8.10 Hashtable
对比 HashMap
因hashtable多了些处理冲突的操作,所以多线程性能速度什么的没有hashmap高。
8.11 迭代器
所用增强for,若在中间有删除操作很可能报错,下面图报不报错自己分析吧,为啥错记心里吧:
为了可以删,使用迭代器,删除的时候不能使用map删,需要用迭代器删,这样能同步:
8.12 工具类
输出:
数组比较:有位置有关
8.13 问题汇总
1、
报错
2、没有内容啊
3、
输出:改了map,没告诉前面的map.keyset
9、IO
9.1 数据流
A就可以看做是数据源的数据:
输入输出时有个阀门:
9.2 文件流
文件流 :FileInputStream,FileOutputStream
数据源和目的地是文件;
Java中的代码其实是不能直接访问内存、磁盘等内容的,为了有跨平台性。
所以要使用特殊类型的对象(下图紫色圈圈)来关联到文件上进行访问:
1、创建文件对象:
2、文件对象的操作
不存在关联的意思是:传的路径不存在,可以使用file.mkdirs()来根据路径创建一个对应名字的目录,可多级,也可以创建文件(此时路径是一个不存在的文件路径)
文件操作:
文件夹操作:
部分输出内容如下:
9.3 文件复制
管道打开了,记得用完关了。至此基本的数据流转的路线建立好了,下面加上输入输出操作:
注意阀门自动的就是一开一关,读一个字节就关了,写一个也就自动关了。发现了吧,太罗嗦了,然后就是读完了还读咋办,运行不报错,但输出的文件中出现错误字符,改:
太罗嗦改进:
9.4 缓冲流
字节缓冲流 :BufferedInputStream,BufferedOutputStream
上面方式使一个字节一个字节读取写入的,使用缓冲区可以多个一起
创建缓冲流对象并与文件输入输出流连接(文件输入输出流是一次传1byte),创建一个缓冲区:
然后读写数据(读的数据存在了缓冲区中,写的时候从中一起取出)
buffOut这一行的意思是:从cache中取,从头开始取出,第三个参数是data就是读了多少写多少???
9.5 字符流
BufferedReader,BufferedWriter(写入可使用更方便的PrintWriter)
一次读一行,写一行,所以可以说读取的不再是字节,而是一个字符串。
刷写数据:是将现在缓冲区的数据都写入文件。flush 缓冲区满不满都写入。
以上三种:字节流 缓冲流 字符流 可以对比记忆,可参考吃透Java IO:字节流、字符流、缓冲流 - 知乎 (zhihu.com)
9.6 序列化
一个对象写入文件(会将其转为一串字节写入,序列化),从文件中读对象(多字节->对象,反序列化)
不是所有对象都能被JVM自动序列化,该对象需要接上一个序列化的接口:
写入:
读:
这个有编译报错,是因为那一步可以有错,但这个错没有被处理,把下面的catch的范围扩大一下就好了:IOException改为Exception。
9.7 常见异常
注意IO操作有很多可能报错的情况,所以需要对可能的错误进行处理:
如 创建流对象,传参时可能错误,所以一般try外创建一个null对象,try内部再new并传参;
read可能错;
write错:
close错....
一般我们都会讲这些可能的错误都写到try中catch一下,并在final中将该关的对象都close了,具体什么错误编译的时候回提示。
10、线程
10.1 线程中的进程
进程:运行中的程序,不是执行中,因为程序是由CPU中的中央处理器读取,再由内部的核心处理器执行(简称CPU内核)的,程序分配到CPU才会执行,所以CPU只有一个内核,即单核CPU,一次只能处理一个程序代码。
cmd命令窗口,使用jps,查看当前的Java运行的进程有哪些
10.2 进程中的线程
一个进程中可以有对个线程,但至少有一个线程。
10.3 创建线程
线程肯定要完成一定工作,所以要先定义工作,写一个线程类来定义工作内容,这个类需要重写父类的run函数,重写的内容就是要执行的工作:
然后创建,并启动:
若直接使用Thread创建线程并启动,没有任务相当于线程没开始工作。
即使是把划红线这一行放在自定义线程后面,也会先打印main:
因为两个线程互不干扰,并行前进,其实是谁快先打印谁,所以即使先启动了 t 线程,但main线程没有停止,一直在运行所以还是会比 t 先打印出来
10.4 线程的生命周期
6个状态:定义在Thread.java文件中
10.5 线程的执行方式(串行,并发)
并发:
此时三个线程并发(t1 t2 main)先打印出谁看谁执行快呗!
串行:加一步操作
10.6 线程休眠
Thread.sleep(1000); 这个函数在哪个线程中被调用哪个线程就会休眠1000ms,进入定时等待状态
10.7 工作
线程工作:每次需要一个线程来工作时都创建一个子类,需要多个线程就要创建多个子类,太麻烦了,所以可使用简便方法:不创建子类,直接使用Thread类,创建其对象时直接把要该线程进行的工作传进去,然后使用start函数开始执行
还有一种传入工作的方式,使用runable接口(接口可以使用匿名类实现):
没什么需要的属性时,可以使用上面两种直接的方式!!
10.8 线程池
使用submit提交作业,此时线程池满了,所以紫色线程任务阻塞。
现在说的是第一种情况,代码实现如下:
2.第二种
并不是说几个任务就几个线程,如果传入第n个任务时,前面创建的线程已经空下来了,可以直接使用它,不需要创建新的,因而是根据需求动态创建。
3. 单一线程
五个任务 但可以看出都是用的是同一个线程。使用场景:工作要一定顺序时。
4. 定时调度线程
10.9 同步 synchronized
多个进程要同时访问一个同步函数或属性,那么要一个个来,目的是同步操作。
synchronized还可以修饰代码块,以顾客去银行取号为例:
顾客类:
银行类:
10.10 阻塞
wait是等待 vs sleep休眠 :
10.11 安全问题
11、反射
11.1 基础
使用反射可以让对象知道自己到底是什么类的对象,可解决多态问题:如下图中user只能使用User中的方法,不能用Child的,主要原因是它不知道自己是Child对象,以为自己是User,所以要使用getClass获取一个类对象,这是Child对象:
通过这个类对象,可以使用很多方法来获取一些关于user身份的内容:
11.2 类加载器
ClassLoader
加载一般是有顺序的,↑ ,所以类加载器也有一个parent的概念,可以通过getparent获取平台类加载器,但是启动类其实是OS平台实现的,所以get不到,返回值为null
输出:因为
11.3 练习
员工登录功能——使用反射实现:
对于员工类不再直接使用new创建对象,使用反射的方式:
先获取员工类的类信息;
然后获取这个类的构造函数,返回一个构造函数的对象;
使用构造函数对象创建一个对象;
还要获取类的属性,返回属性对象;
接着通过属性对象给属性赋值(这里需要指明是哪一个类,赋的值是什么);
获取需要的类的方法;
调用方法(指明哪个类,可能有返回值哦)
main中:
最后打印出result的内容:System.out.println(result); 输出:false
11.4 常见异常
获取类信息其实有三种方法:
只不过方法3可能出现异常,即下图6个异常中第一个。
下图是上面整过过程中可能出现的异常,只不过异常太多了,所以直接在main中throws了Exception,没再细化。