碎碎念~
终于来到了最后一堆啦,但其实好像前面的知识也没有消化地很好,但是会在不断地码代码的过程中好好吸收消化这些知识滴~
完结撒花~
在末尾的那一些知识其实自己还是没有理解好,最主要的是怎么把知识转化到在代码上展现出来,今天琢磨课程设计琢磨地有点秃呜呜呜,一团浆糊
再说明一下,以下的笔记都是来自动力节点杜老师的网课外加自己的一些理解噢,如果有不对的地方欢迎评论区指出~
目录
关于Java.util.Collection接口中的常用的方法
IO流
完整类名带包名(这句话在心里形成印象)
1、访问控制权限
1.1、访问控制权限都有哪些?
4个。
private 私有
public 公开
protected 受保护
默认(default)啥也不写
1.2、以上的4个访问控制权限:控制的范围是什么?
private 表示私有的,只能在本类中访问
public 表示公开的,在任何位置都可以访问(所以说不同包也可以?
“默认”表示只能在本类,以及同包下访问。
protected表示只能在本类、同包、子类中访问。
访问控制修饰符 本类 同包 子类 任意位置
---------------------------------------------------------------------------
public 可以 可以 可以 可以
protected 可以 可以 可以 不行
默认 可以 可以 不行 不行
private 可以 不行 不行 不行
这个不要死记硬背,自己下去之后编写代码自己测试。
范围从大到小排序:public > protected > 默认 > private
1.3、访问控制权限修饰符可以修饰什么?
属性(4个都能用)
方法(4个都能用)
类(public和默认能用,其它不行。)
接口(public和默认能用,其它不行。)
.....
2、JDK类库的根类:Object
2.1、这个老祖宗类中的方法我们需要先研究一下,因为这些方法都是所有子类通用的。
任何一个类默认继承Object。就算没有直接继承,最终也会间接继承。
2.2、Object类当中有哪些常用的方法?
我们去哪里找这些方法呢?
第一种方法:去源代码当中。(但是这种方式比较麻烦,源代码也比较难)
第二种方法:去查阅java的类库的帮助文档。
什么是API?
应用程序编程接口。(Application Program Interface)
整个JDK的类库就是一个javase的API。
每一个API都会配置一套API帮助文档。
SUN公司提前写好的这套类库就是API。(一般每一份API都对应一份API帮助文档。)
目前为止我们只需要知道这几个方法即可:
protected Object clone() // 负责对象克隆的。
int hashCode() // 获取对象哈希值的一个方法。
boolean equals(Object obj) // 判断两个对象是否相等
String toString() // 将对象转换成字符串形式
protected void finalize() // 垃圾回收器负责调用的方法
2.3、toString()方法
1、源代码长什么样?
public String toString(){
return this.getClass().getName()+“@”+Integer.toHexString(hashCode());
}
源代码上toString()方法的默认实现是:
类名@对象的内存地址转化为十六进制的形式
toString()方法设计的目的是:通过调用这个方法可以将一个“Java对象”转换成“字符串”形式
以后所有类的toString()方法是需要重写的。
重写规则,越简单越明了就好。
System.out.println(引用); 这里会自动调用“引用”的toString()方法。
输出引用的时候,会自动调用该引用的toString方法
String类是SUN写的,toString方法已经重写了。
2.4、equals()方法
public boolean equals(Object obj){
return(this==obj);
}
以上这个方法是Object类的默认实现
Sun公司设计equals方法的目的是什么?
以后编程的过程当中,都要通过equals方法来判断两个对象是否相等
equals方法是判断两个对象是否相等的
判断两个基本数据类型的数据是否相等直接使用“==”就行
判断两个Java对象是否相等,不能使用“==”,因为双等号比较的是两个对象的内存地址
在object类中的equals方法中,默认采用的是双等号判断两个Java对象是否相等,而双等号判断的两个Java对象的内存地址,我们应该判断两个Java对象的内容是否相等,所以老祖宗的equals方法不够用,需要子类重写
以后所有类的equals方法也需要重写,因为Object中的equals方法比较
的是两个对象的内存地址,我们应该比较内容,所以需要重写。
重写规则:自己定,主要看是什么和什么相等时表示两个对象相等。
基本数据类型比较实用:==
对象和对象比较:调用equals方法
String类是SUN编写的,所以String类的equals方法重写了。
以后判断两个字符串是否相等,最好不要使用==,要调用字符串对象的equals方法。
注意:重写equals方法的时候要彻底。
1、String类已经重写了equals方法,比较两个字符串不能使用==,必须使用equals,equals是通用的
String类已经重写了toString()方法
Java中的基本数据类型比较是否相等用双等号,引用类型判断相等用equals方法
2.5、finalize()方法。
这个方法是protected修饰的,在Object类中这个方法的源代码是?
protected void finalize() throws Throwable { }
hashCode方法:
在Object中的hashCode方法是怎样的?
public native int hashCode();
这个方法不是抽象方法,带有native关键字,底层调用c++程序
hashCode()方法返回的是哈西码:
实际上就是一个Java对象的内存地址,经过哈西算法,得出的一个值
所以该方法的执行结果可以等同看做一个Java对象的内存地址
3、匿名内部类
Java语言中的数组是一种引用数据类型,不属于基本数据类型,数组的父类是object
数组实际上是一个容器,可以同时容纳多个元素(数组是一个数据的集合)
数组:字面意思是“一组数据”
数组当中可以存储“基本数据类型”的数据,也可以存储“引用数据类型”的数据
数组因为是引用类型,所以数组对象是堆内存当中(数组是存储在堆当中的)
数组当中如果存储的是“Java对象”的话,实际上存储的是对象的“引用(内存地址)”
数组一旦创建,在Java中规定,长度不可变(数组长度不可变)
Java中的数组要求数组中元素的类型要统一
所有的数组对象都有Length属性(Java自带的),用来获取数组中元素的个数
所有的数组都是拿“第一个小方框的内存地址”作为整个数组对象的内存地址。(数组中首元素的内存地址作为整个数组对象)
数组中每个元素都有下标,第一个元素的下标是1,最后一个元素的下标是:Length-1
下标非常重要,因为我们对数组中元素进行“存取”的时候,都要用到下标
1、数组
1.1、数组的优点和缺点,并且要理解为什么。
第一:空间存储上,数组中的元素内存地址是连续的(这是数组存储元素的特点)。数组实际上是一种简单的数据结构
第二:每个元素占用的空间大小相同。
第三:知道首元素的内存地址。
第四:通过下标可以计算出偏移量。
通过一个数学表达式,就可以快速计算出某个下标位置上元素的内存地址,
直接通过内存地址定位,效率非常高。
因为以上四点 优点:检索效率高。(查询某个下标上的元素时效率极高,可以说是查询效率最高的一个数据结构)
缺点:由于为了保证数组中每个元素的内存地址连续,随机增删效率较低,因为随机增删元素会涉及到后面元素统一向前或向后位移的操作,
数组无法存储大数据量。因为很难在内存空间上找到特别大的连续的内存空间
注意:数组最后一个元素的增删效率不受影响。
每一个元素的内存地址在空间存储上是连续的
每一个元素类型相同,所以占用空间大小一样
1.2、一维数组的静态初始化和动态初始化
静态初始化:
int[] arr = {1,2,3,4};
Object[] objs = {new Object(), new Object(), new Object()};
动态初始化:
int[] arr = new int[4]; // 初始化一个4个长度的int类型数组,每个元素默认值0
Object[] objs = new Object[4]; // 4个长度,每个元素默认值null(位置先占上,不存东西)
String【】 names=new String【6】
当你确定数组中存储哪些具体的元素时,采用静态初始化方式
不确定的时候,就用动态初始化
后期赋值,注意下标别越界
main方法上面的“String【】 args”有什么用?
谁负责调用main方法(JVM)
JVM调用main方法的时候,会自动传一个String数组过来
主要是用来接收用户输入参数的
1.3、一维数组的遍历
for(int i = 0; i < arr.length; i++){
System.out.println(arr[i]);
}
下标越界:下标越界异常
从最后一个元素遍历到第一个元素
for(int i = a.length-1;i>0;i--)//颠倒顺序输出
关于一位数组的扩容
在Java开发中,数组长度一旦确定不可变,那么数组满了怎么办?
先创建一个大容量的数组,再将小容量的数组拷贝进去
数组扩容效率较低,最好预估准确,可以减少数组的扩容次数
System.arrayCopy
二维数组其实是一个特殊的一维数组,特殊在这个一维数组当中的每一个元素是一个一维数组
三维数组是一个特殊的二维数组
a【二维数组中的一维数组的下标】【一维数组的下标】
a【0】【0】:表示第一个一维数组中的第一个元素
1.4、二维数组的静态初始化和动态初始化
静态初始化:
int[][] arr = {
{1,2,34},
{54,4,34,3},
{2,34,4,5}
};
Object[][] arr = {
{new Object(),new Object()},
{new Object(),new Object()},
{new Object(),new Object(),new Object()}
};
动态初始化:
int[][] arr = new int[3][4];
Object[][] arr = new Object[4][4];
Animal[][] arr = new Animal[3][4];
// Person类型数组,里面可以存储Person类型对象,以及Person类型的子类型都可以。
Person[][] arr = new Person[2][2];
....
1.5、二维数组的遍历
for(int i = 0; i < arr.length; i++){ // 外层for循环负责遍历外面的一维数组。(外层循环三次,负责纵向)
// 里面这个for循环负责遍历二维数组里面的一维数组。
for(int j = 0; j < arr[i].length; j++){
System.out.print(arr[i][j]);
}
// 换行。(相当于一维数组遍历完了要换行)
System.out.println();
}
1.6、main方法上“String[] args”参数的使用(非重点,了解一下,以后一般都是有界面的,用户可以在界面上输入用户名和密码等参数信息。)
1.7、数组的拷贝:System.arraycopy()方法的使用
数组有一个特点:长度一旦确定,不可变。
所以数组长度不够的时候,需要扩容,扩容的机制是:新建一个大数组,
将小数组中的数据拷贝到大数组,然后小数组对象被垃圾回收。
1.8、对数组中存储引用数据类型的情况,要会画它的内存结构图。
2.1、常见的算法:
排序算法:
冒泡排序算法
选择排序算法
查找算法:
二分法查找
以上算法在以后的java实际开发中我们不需要使用的。
因为java已经封装好了,直接调用就行。
只不过以后面试的时候,可能会有机会碰上。
2.2、算法实际上在java中不需要精通,因为java中已经封装好了,
要排序就调用方法就行。例如:java中提供了一个数组工具类:
java.util.Arrays
Arrays是一个工具类。
其中有一个sort()方法,可以排序。静态方法,直接使用类名调用就行。
3、冒泡排序:
参与比较的数据:9 8 10 7 6 0 11
第1次循环:
8 9 10 7 6 0 11 (第1次比较:交换)
8 9 10 7 6 0 11 (第2次比较:不交换)
8 9 7 10 6 0 11 (第3次比较:交换)
8 9 7 6 10 0 11 (第4次比较:交换)
8 9 7 6 0 10 11 (第5次比较:交换)
8 9 7 6 0 10 11 (第6次比较:不交换)
最终冒出的最大数据在右边:11
参与比较的数据:8 9 7 6 0 10
第2次循环:
8 9 7 6 0 10(第1次比较:不交换)
8 7 9 6 0 10(第2次比较:交换)
8 7 6 9 0 10(第3次比较:交换)
8 7 6 0 9 10(第4次比较:交换)
8 7 6 0 9 10(第5次比较:不交换)
参与比较的数据:8 7 6 0 9
第3次循环:
7 8 6 0 9(第1次比较:交换)
7 6 8 0 9(第2次比较:交换)
7 6 0 8 9(第3次比较:交换)
7 6 0 8 9(第4次比较:不交换)
参与比较的数据:7 6 0 8
第4次循环:
6 7 0 8(第1次比较:交换)
6 0 7 8(第2次比较:交换)
6 0 7 8(第3次比较:不交换)
参与比较的数据:6 0 7
第5次循环:
0 6 7(第1次比较:交换)
0 6 7(第2次比较:不交换)
参与比较的数据:0 6
第6次循环:
0 6 (第1次比较:不交换)
for(int i = 6; i > 0; i--){ // 6次
//7条数据比6次
//6条数据比5次
//5条数据比4次
//4条数据比3次
//3条数据比2次
//2条数据比1次
for(int j = 0; j < i; j++){
}
}
4、选择排序:
选择排序比冒泡排序的效率高。
高在交换位置的次数上。
选择排序的交换位置是有意义的。
循环一次,然后找出参加比较的这堆数据中最小的,拿着这个最小的值和
最前面的数据“交换位置”。
参与比较的数据:3 1 6 2 5 (这一堆参加比较的数据中最左边的元素下标是0)
第1次循环之后的结果是:
1 3 6 2 5
参与比较的数据:3 6 2 5 (这一堆参加比较的数据中最左边的元素下标是1)
第2次循环之后的结果是:
2 6 3 5
参与比较的数据:6 3 5 (这一堆参加比较的数据中最左边的元素下标是2)
第3次循环之后的结果是:
3 6 5
参与比较的数据:6 5 (这一堆参加比较的数据中最左边的元素下标是3)
第4次循环之后的结果是:
5 6
注意:5条数据,循环4次。
5、二分法查找(折半查找):
第一:二分法查找建立在排序的基础之上。
第二:二分法查找效率要高于“一个挨着一个”的这种查找方式。
第三:二分法查找原理?
10(0下标) 23 56 89 100 111 222 235 500 600(下标9) arr数组
目标:找出600的下标
(0 + 9) / 2 --> 4(中间元素的下标)
arr[4]这个元素就是中间元素:arr[4]是 100
100 < 600
说明被查找的元素在100的右边。
那么此时开始下标变成:4 + 1
(5 + 9) / 2 --> 7(中间元素的下标)
arr[7] 对应的是:235
235 < 600
说明被查找的元素在235的右边。
开始下标又进行了转变:7 + 1
(8 + 9) / 2 --> 8
arr[8] --> 500
500 < 600
开始元素的下标又发生了变化:8 + 1
(9 + 9) / 2 --> 9
arr[9]是600,正好和600相等,此时找到了。
6、介绍一下java.util.Arrays工具类。
所有方法都是静态的,直接用类名调用
主要使用的是两个方法:
二分法查找,排序
以后要看文档,不要死记硬背。
1、String类。
String表示字符串类型,属于引用数据类型,不属于基本数据类型
在Java中随便使用双引号括起来的都是String对象,例如"abc"
Java中规定,双引号括起来的字符串,是不可变的,也就是说"abc"生死不变
在JDK当中双引号括起来的字符串,例如“abc”都是直接存储在“方法区”的“字符串常量池”当中的
因为字符串在实际的开发中使用太频繁,为了执行效率,所以把字符串放到了方法区的字符串常量池当中
1.1、对String在内存存储方面的理解:
第一:字符串一旦创建不可变。
第二:双引号括起来的字符串存储在字符串常量池中。
第三:字符串的比较必须使用equals方法。(用双等号不保险)
第四:String已经重写了toString()和equals()方法。
1.2、String的构造方法。(没听懂)
String s = "abc";
String s = new String("abc");
String s = new String(byte数组);
String s = new String(byte数组, 起始下标, 长度);
String s = new String(char数组);
String s = new String(char数组, 起始下标, 长度);
1.3、String类常用的21个方法。
2、StringBuffer/StringBuilder
消耗内存
2.1、StringBuffer/StringBuilder可以看做可变长度字符串。
2.2、StringBuffer/StringBuilder初始化容量16.
2.3、StringBuffer/StringBuilder是完成字符串拼接操作的,方法名:append
2.4、StringBuffer是线程安全的。StringBuilder是非线程安全的。
2.5、频繁进行字符串拼接不建议使用“+”
包装类
3、八种基本数据类型对应的包装类
3.1、包装类存在有什么用?
方便编程。
3.2、八种包装类的类名是什么?
Byte
Short
Integer
Long
Float
Double
Boolean
Character
3.3、所有数字的父类Number
3.4、照葫芦画瓢:学习Integer,其它的模仿Integer。
3.5、什么是装箱?什么是拆箱?
池:cache,就是缓存机制
缓存优点:效率高
缓存缺点:耗费内存
引用数据类型转化成基本数据类型(拆箱)
基本数据类型转化成引用数据类型(装箱)
1、八种基本数据类型对应的包装
八中包装类中其中6个都是数字对应的包装类,他们的父类都是Number,可以先研究一下Number中公共的方法
number是一个抽象类,无法实例化对象
Number类中有这样的方法:
byte byteValue()
以byte形式返回指定的数值
abstract double doubleValue()
以double 形式返回指定的数值
abstract float floatValue()
以float形式返回指定的数值
abstract int intValue()
以int 形式返回指定的数值
abstract long longValue()
以long形式返回指定的数值
shart shortValue()
以short形式返回指定的数值
这些方法其实所有的数字包装类的子类都有,这些方法是负责拆箱的
1.1、什么是自动装箱和自动拆箱,代码怎么写?
好处是方便编程
自动装箱(int类型自动转换为integer)
基本数据类型自动转换成包装类
自动拆箱:包装类自动转换成基本数据类型
有了自动拆箱后,Number类中的方法就用不着了
Integer x = 100; // x里面并不是保存100,保存的是100这个对象的内存地址。
Integer y = 100;
System.out.println(x == y); // true
Integer x = 128;
Integer y = 128;
System.out.println(x == y); // false
1.2、Integer类常用方法。
Integer.valueOf()
Integer.parseInt("123")
Integer.parseInt("中文") : NumberFormatException
1.3、Integer String int三种类型互相转换。
2、日期类
2.1、获取系统当前时间
Date d = new Date();
2.2、日期格式化:Date --> String
yyyy-MM-dd HH:mm:ss SSS
SimpleDateFormat sdf = new SimpleDate("yyyy-MM-dd HH:mm:ss SSS");
String s = sdf.format(new Date());
2.3、String --> Date
SimpleDateFormat sdf = new SimpleDate("yyyy-MM-dd HH:mm:ss");
Date d = sdf.parse("2008-08-08 08:08:08");
2.4、获取毫秒数
long begin = System.currentTimeMillis();
Date d = new Date(begin - 1000 * 60 * 60 * 24);
3、数字类
3.1、DecimalFormat数字格式化
###,###.## 表示加入千分位,保留两个小数。
###,###.0000 表示加入千分位,保留4个小数,不够补0
3.2、BigDecimal
财务软件中通常使用BigDecimal
4、随机数
4.1、怎么产生int类型随机数。
Random r = new Random();
int i = r.nextInt();
4.2、怎么产生某个范围之内的int类型随机数。
Random r = new Random();
int i = r.nextInt(101); // 产生[0-100]的随机数。
5、枚举
5.1、枚举是一种引用数据类型。
5.2、枚举编译之后也是class文件。
5.3、枚举类型怎么定义?
enum 枚举类型名{
枚举值,枚举值2,枚举值3
}
5.4、当一个方法执行结果超过两种情况,并且是一枚一枚可以列举出来
的时候,建议返回值类型设计为枚举类型。
总结一下之前所学的经典异常:
空指针异常:NullPointerException
类型转换异常:ClassCastException
数组下标越界异常:IndexOutOfBoundsException
什么是异常,Java提供异常处理机制有什么用?
以下程序执行过程中发生了不正常的情况,而这种不正常的情况叫做:异常
Java语言是很完善的语言,提供了异常的处理方式,以下程序执行过程中出出现了不正常情况,那把把该异常信息打印输出到控制台,供程序员参考。程序员看到异常信息之后,可以对程序进行修改,让程序更加的健壮
0、异常处理机制
0.1、java中异常的作用是:增强程序健壮性。
0.2、java中异常以类和对象的形式存在。
1、java的异常处理机制
1.1、异常在java中以类和对象的形式存在。每一个异常类都可以创建异常对象
那么异常的继承结构是怎样的?
我们可以使用UML图来描述一下继承结构。
画UML图有很多工具,例如:Rational Rose(收费的)、starUML等....
Object
Object下有Throwable(可抛出的)
Throwable下有两个分支:Error(不可处理,直接退出JVM)和Exception(可处理的)
Exception下有两个分支:
Exception的直接子类:编译时异常(要求程序员在编写程序阶段必须预先对这些异常进行处理,如果不处理编译器报错,因此得名编译时异常。)。
RuntimeException:运行时异常。(在编写程序阶段程序员可以预先处理,也可以不管,都行。)
1.2、编译时异常和运行时异常,都是发生在运行阶段。编译阶段异常是不会发生的。
编译时异常因为什么而得名?
因为编译时异常必须在编译(编写)阶段预先处理,如果不处理编译器报错,因此得名。
所有异常都是在运行阶段发生的。因为只有程序运行阶段才可以new对象。
因为异常的发生就是new异常对象。
1.3、编译时异常和运行时异常的区别?
编译时异常一般发生的概率比较高。
举个例子:
你看到外面下雨了,倾盆大雨的。
你出门之前会预料到:如果不打伞,我可能会生病(生病是一种异常)。
而且这个异常发生的概率很高,所以我们出门之前要拿一把伞。
“拿一把伞”就是对“生病异常”发生之前的一种处理方式。
对于一些发生概率较高的异常,需要在运行之前对其进行预处理。
运行时异常一般发生的概率比较低。
举个例子:
小明走在大街上,可能会被天上的飞机轮子砸到。
被飞机轮子砸到也算一种异常。
但是这种异常发生概率较低。
在出门之前你没必要提前对这种发生概率较低的异常进行预处理。
如果你预处理这种异常,你将活的很累。
突然感觉Java也太可爱了吧哈哈哈哈哈哈
假设你在出门之前,你把能够发生的异常都预先处理,你这个人会更加
的安全,但是你这个人活的很累。
假设java中没有对异常进行划分,没有分为:编译时异常和运行时异常,
所有的异常都需要在编写程序阶段对其进行预处理,将是怎样的效果呢?
首先,如果这样的话,程序肯定是绝对的安全的。
但是程序员编写程序太累,代码到处都是处理异常
的代码。
1.4、编译时异常还有其他名字:
受检异常:CheckedException
受控异常
1.5、运行时异常还有其它名字:
未受检异常:UnCheckedException
非受控异常
1.6、再次强调:所有异常都是发生在运行阶段的。
1.7、Java语言中对异常的处理包括两种方式:
第一种方式:在方法声明的位置上,使用throws关键字,抛给上一级。
谁调用我,我就抛给谁。抛给上一级。
第二种方式:使用try..catch语句进行异常的捕捉。
这件事发生了,谁也不知道,因为我给抓住了。
举个例子:
我是某集团的一个销售员,因为我的失误,导致公司损失了1000元,
“损失1000元”这可以看做是一个异常发生了。我有两种处理方式,
第一种方式:我把这件事告诉我的领导【异常上抛】
第二种方式:我自己掏腰包把这个钱补上。【异常的捕捉】
张三 --> 李四 ---> 王五 --> CEO
思考:
异常发生之后,如果我选择了上抛,抛给了我的调用者,调用者需要
对这个异常继续处理,那么调用者处理这个异常同样有两种处理方式。
1.8、注意:Java中异常发生之后如果一直上抛,最终抛给了main方法,main方法继续
向上抛,抛给了调用者JVM,JVM知道这个异常发生,只有一个结果。终止java程序的执行。
2、什么是UML?有什么用?
UML是一种统一建模语言。
一种图标式语言(画图的)
UML不是只有java中使用。只要是面向对象的编程语言,都有UML。
一般画UML图的都是软件架构师或者说是系统分析师。这些级别的人员使用的。
软件设计人员使用UML。
在UML图中可以描述类和类之间的关系,程序执行的流程,对象的状态等.
盖大楼和软件开发一样,一个道理。
盖楼之前,会先由建筑师画图纸。图纸上一个一个符号都是标准符号。
这个图纸画完,只要是搞建筑的都能看懂,因为这个图纸上标注的这些
符号都是一种“标准的语言”。
在java软件开发当中,软件分析师/设计师负责设计类,java软件开发人员
必须要能看懂。
1、集合概述
1.1、什么是集合?有什么用?
数组其实就是一个集合。集合实际上就是一个容器。可以来容纳其它类型的数据。
集合为什么说在开发中使用较多?
集合是一个容器,是一个载体,可以一次容纳多个对象。
在实际开发中,假设连接数据库,数据库当中有10条记录,
那么假设把这10条记录查询出来,在java程序中会将10条
数据封装成10个java对象,然后将10个java对象放到某一个
集合当中,将集合传到前端,然后遍历集合,将一个数据一个
数据展现出来。
集合起到一个承载的作用
1.2、集合不能直接存储基本数据类型,另外集合也不能直接存储java对象,
集合当中存储的都是java对象的内存地址。(或者说集合中存储的是引用。)
list.add(100); //自动装箱,转成Integer类型放到了list里面
注意:
集合在java中本身是一个容器,是一个对象,也有内存地址
集合中任何时候存储的都是对象的“引用”。
1.3、在java中每一个不同的集合,底层会对应不同的数据结构。往不同的集合中
存储元素,等于将数据放到了不同的数据结构当中。
什么是数据结构?
数据存储的结构就是数据结构。不同的数据结构,数据存储方式不同。例如:
数组、二叉树、链表、哈希表...
以上这些都是常见的数据结构。
你往集合c1中放数据,可能是放到数组上了。
你往集合c2中放数据,可能是放到二叉树上了。
.....
你使用不同的集合等同于使用了不同的数据结构。
你在java集合这一章节,你需要掌握的不是精通数据结构。java中已经将数据结构
实现了,已经写好了这些常用的集合类,你只需要掌握怎么用?在什么情况下选择
哪一种合适的集合去使用即可。
new ArrayList(); 创建一个集合,底层是数组。
new LinkedList(); 创建一个集合对象,底层是链表。
new TreeSet(); 创建一个集合对象,底层是二叉树。
.....
1.4、集合在java JDK中哪个包下?
java.util.*;
所有的集合类和集合接口都在java.util包下。
1.5、为了让大家掌握集合这块的内容,最好能将集合的继承结构图背会!!!
集合整个这个体系是怎样的一个结构,你需要有印象。
1.6、在java中集合分为两大类:
一类是单个方式存储元素:
单个方式存储元素,这一类集合中超级父接口:java.util.Collection;
一类是以键值对儿的方式存储元素
以键值对的方式存储元素,这一类集合中超级父接口:java.util.Map;
所有集合继承terable的意思是:所有集合都是可迭代的(可遍历的)
2、总结重点:
第一个重点:把集合继承结构图背会。
第二个重点:把Collection接口中常用方法测试几遍。
第三个重点:把迭代器弄明白。
第四个重点:Collection接口中的remove方法和contains方法底层都会调用equals,
这个弄明白。
关于Java.util.Collection接口中的常用的方法
1、Collection中能存放什么元素?
没有使用"泛型"之前,Collection中可以存储object的所有子类型
使用了"泛型"之后,Collection中只能存储某个具体的类型
集合后期我们会学习“泛型”语法,目前先不用管,Collection中什么都能存
只要是Object的子类型就行(集合中不能直接存储基本数据类型,也不能存储Java对象,知识存储Java对象的内存地址)
Collection中的常用方法
boolean add(Object e)向集合中添加元素
int size()获取集合中元素的个数
c.size();
void clear()清空集合
c.add();
boolean contains(Object o)判断当前集合中是否包含元素o,包含返回ture,不包含返回false
boolean flag = c.contains(“绿巨人”);
System.out.println(flag);//
boolean remove(Object o) 删除集合中的某个元素
c.remove()
boolean isEmpty()判断该集合中元素的个数是否为0
c.isEmpty()
Object【】 toArray() 调用这个方法可以把集合转换成数组
Object【】 objs = c.toArray();
迭代器遍历集合
关于集合遍历/迭代专题
以下讲解的遍历方式是所有Collection通用的一种方式
在Map集合中不能用,在所有的Collection以及子类中使用
第一步:获取集合对象的迭代器对象Iterator
Iterator it = c.iterator();//迭代器对象it,最初并没有指向第一个元素
第二部:通过以上获取的迭代器对象Iterator中的方法:
boolean hasNext()如果仍有元素可以迭代,则返回true
Object next() 返回迭代的下一个元素
boolean hasNext = it.hasNext();这个方法返回true,表示还有元素可以迭代,这个方法返回false表示没有更多的元素可以迭代了
Object obj = it.next();这个方法让迭代器前进一位,并且将指向的元素返回(拿到)
boolean hasNext = it.hasNext();
if(hasNext){
//不管你当初存进去什么,取出来统一都是object
Object obj = it.next();
System.out.println(obj);
}
关于集合元素的remove
深入Collection集合的contains方法
boolean contains(Object o)
判断集合中是否包含某个对象o
如果包含返回true,如果不包含返回false
contains方法是用来判断集合中是否包含某个元素的方法
调用了equals方法进行比对
rquals方法返回ture,就表示包含这个元素
list集合存储元素特点:有序可重复(存进去的顺序和取出来的顺序一样)不是说按照大小排序的意思
有序:List集合中的元素有下标
从0开始,以1递增
可重复:存储一个1,还可以再存储1
set集合:无序不可重复(存进去是这个顺序,取出来就不一定了)
该集合中元素没有下标
1、List接口中的常用方法。
List是Collection接口的子接口。所以List接口中有一些特有的方法。
void add(int index, Object element)
Object set(int index, Object element)
Object get(int index)
int indexOf(Object o)
int lastIndexOf(Object o)
Object remove(int index)
参数是object
因为有下标,所以List集合有自己比较特殊的遍历方式
2、迭代器迭代元素的过程中不能使用集合对象的remove方法删除元素,
要使用迭代器Iterator的remove方法来删除元素,防止出现异常:
ConcurrentModificationException
3、ArrayList
ArrayList集合初始化容量10
扩容为原容量1.5倍。
底层是数组。
数组优点和缺点要能够说出来!
另外要注意:ArrayList集合末尾增删元素效率还是可以的。
4、链表数据结构
第一:单向链表和双向链表数据结构要理解。
第二:链表数据结构的优点和缺点要能够说出来。
5、Vector:
Vector初始化容量是10.
扩容为原容量的2倍。
底层是数组。
Vector底层是线程安全的。
怎么得到一个线程安全的List:
Collections.synchronizedList(list);
6、JDK5.0新特性:泛型
第一:集合使用泛型来减少向下转型的操作。
第二:怎么使用泛型?
第三:怎么自定义泛型?
7、JDK5.0新特性:
foreach
对数组怎么遍历?
for(int i : arr){
System.out.println(i);
}
对集合怎么遍历?
for(String s : list){
System.out.println(s);
}
8、JDK8新特性:钻石表达式
List<String> list = new ArrayList<>();
类型自动推断!
1、掌握Map接口中常用方法。
2、遍历Map集合的两种方式都要精通。
第一种:获取所有key,遍历每个key,通过key获取value.
第二种:获取Set<Map.Entry>即可,遍历Set集合中的Entry
调用entry.getKey() entry.getValue()
3、了解哈希表数据结构。
4、存放在HashMap集合key部分和HashSet集合中的元素需要同时重写hashCode和equals。
5、HashMap和Hashtable的区别。
HashMap:
初始化容量16,扩容2倍。
非线程安全
key和value可以为null。
Hashtable
初始化容量11,扩容2倍+1
线程安全
key和value都不能是null。
6、Properties类的常用两个方法。
setProperty
getProperty
7、了解自平衡二叉树数据结构。
左小右大原则存储。
中序遍历方式。
8、TreeMap的key或者TreeSet集合中的元素要想排序,有两种实现方式:
第一种:实现java.lang.Comparable接口。
第二种:单独编写一个比较器Comparator接口。
9、集合工具类Collections:
synchronizedList方法
sort方法(要求集合中元素实现Comparable接口。)
1、集合这块最主要掌握什么内容?
1.1、每个集合对象的创建(new)
1.2、向集合中添加元素
1.3、从集合中取出某个元素
1.4、遍历集合
1.5、主要的集合类:
ArrayList
LinkedList
HashSet (HashMap的key,存储在HashMap集合key的元素需要同时重写hashCode + equals)
TreeSet
HashMap
Properties
TreeMap
2、IO流,什么是IO?
I : Input
O : Output
通过IO可以完成硬盘文件的读和写。
3、IO流的分类?
有多种分类方式:
一种方式是按照流的方向进行分类:
以内存作为参照物,
往内存中去,叫做输入(Input)。或者叫做读(Read)。
从内存中出来,叫做输出(Output)。或者叫做写(Write)。
另一种方式是按照读取数据方式不同进行分类:
有的流是按照字节的方式读取数据,一次读取1个字节byte,等同于一次读取8个二进制位。
这种流是万能的,什么类型的文件都可以读取。包括:文本文件,图片,声音文件,视频文件等....
假设文件file1.txt,采用字节流的话是这样读的:
a中国bc张三fe
第一次读:一个字节,正好读到'a'
第二次读:一个字节,正好读到'中'字符的一半。
第三次读:一个字节,正好读到'中'字符的另外一半。
有的流是按照字符的方式读取数据的,一次读取一个字符,这种流是为了方便读取
普通文本文件而存在的,这种流不能读取:图片、声音、视频等文件。只能读取纯
文本文件,连word文件都无法读取。
假设文件file1.txt,采用字符流的话是这样读的:
a中国bc张三fe
第一次读:'a'字符('a'字符在windows系统中占用1个字节。)
第二次读:'中'字符('中'字符在windows系统中占用2个字节。)
综上所述:流的分类
输入流、输出流
字节流、字符流
4、Java中的IO流都已经写好了,我们程序员不需要关心,我们最主要还是掌握,
在java中已经提供了哪些流,每个流的特点是什么,每个流对象上的常用方法有
哪些????
java中所有的流都是在:java.io.*;下。
java中主要还是研究:
怎么new流对象。
调用流对象的哪个方法是读,哪个方法是写。
5、java IO流这块有四大家族:
四大家族的首领:
java.io.InputStream 字节输入流
java.io.OutputStream 字节输出流
java.io.Reader 字符输入流
java.io.Writer 字符输出流
四大家族的首领都是抽象类。(abstract class)
所有的流都实现了:
java.io.Closeable接口,都是可关闭的,都有close()方法。
流毕竟是一个管道,这个是内存和硬盘之间的通道,用完之后一定要关闭,
不然会耗费(占用)很多资源。养成好习惯,用完流一定要关闭。
所有的输出流都实现了:
java.io.Flushable接口,都是可刷新的,都有flush()方法。
养成一个好习惯,输出流在最终输出之后,一定要记得flush()
刷新一下。这个刷新表示将通道/管道当中剩余未输出的数据
强行输出完(清空管道!)刷新的作用就是清空管道。
注意:如果没有flush()可能会导致丢失数据。
注意:在java中只要“类名”以Stream结尾的都是字节流。以“Reader/Writer”结尾的都是字符流。
6、java.io包下需要掌握的流有16个:
文件专属:
java.io.FileInputStream(掌握)
java.io.FileOutputStream(掌握)
java.io.FileReader
java.io.FileWriter
转换流:(将字节流转换成字符流)
java.io.InputStreamReader
java.io.OutputStreamWriter
缓冲流专属:
java.io.BufferedReader
java.io.BufferedWriter
java.io.BufferedInputStream
java.io.BufferedOutputStream
数据流专属:
java.io.DataInputStream
java.io.DataOutputStream
标准输出流:
java.io.PrintWriter
java.io.PrintStream(掌握)
对象专属流:
java.io.ObjectInputStream(掌握)
java.io.ObjectOutputStream(掌握)
7、java.io.File类。
File类的常用方法。
8、java io这块还剩下什么内容:
第一:ObjectInputStream ObjectOutputStream的使用。
第二:IO流+Properties集合的联合使用。
1、拷贝目录。
2、关于对象流
ObjectInputStream
ObjectOutputStream
重点:
参与序列化的类型必须实现java.io.Serializable接口。
并且建议将序列化版本号手动的写出来。
private static final long serialVersionUID = 1L;
3、IO + Properties联合使用。
IO流:文件的读和写。
Properties:是一个Map集合,key和value都是String类型。
4、多线程
4.1、什么是进程?什么是线程?
进程是一个应用程序(1个进程是一个软件)。
线程是一个进程中的执行场景/执行单元。
一个进程可以启动多个线程。
4.2、对于java程序来说,当在DOS命令窗口中输入:
java HelloWorld 回车之后。
会先启动JVM,而JVM就是一个进程。
JVM再启动一个主线程调用main方法。
同时再启动一个垃圾回收线程负责看护,回收垃圾。
最起码,现在的java程序中至少有两个线程并发,
一个是垃圾回收线程,一个是执行main方法的主线程。
4.3、进程和线程是什么关系?举个例子
阿里巴巴:进程
马云:阿里巴巴的一个线程
童文红:阿里巴巴的一个线程
京东:进程
强东:京东的一个线程
妹妹:京东的一个线程
进程可以看做是现实生活当中的公司。
线程可以看做是公司当中的某个员工。
注意:
进程A和进程B的内存独立不共享。(阿里巴巴和京东资源不会共享的!)
魔兽游戏是一个进程
酷狗音乐是一个进程
这两个进程是独立的,不共享资源。
线程A和线程B呢?
在java语言中:
线程A和线程B,堆内存和方法区内存共享。
但是栈内存独立,一个线程一个栈。
假设启动10个线程,会有10个栈空间,每个栈和每个栈之间,
互不干扰,各自执行各自的,这就是多线程并发。
火车站,可以看做是一个进程。
火车站中的每一个售票窗口可以看做是一个线程。
我在窗口1购票,你可以在窗口2购票,你不需要等我,我也不需要等你。
所以多线程并发可以提高效率。
java中之所以有多线程机制,目的就是为了提高程序的处理效率。
4.4、思考一个问题:
使用了多线程机制之后,main方法结束,是不是有可能程序也不会结束。
main方法结束只是主线程结束了,主栈空了,其它的栈(线程)可能还在
压栈弹栈。
4.5、分析一个问题:对于单核的CPU来说,真的可以做到真正的多线程并发吗?
对于多核的CPU电脑来说,真正的多线程并发是没问题的。
4核CPU表示同一个时间点上,可以真正的有4个进程并发执行。
什么是真正的多线程并发?
t1线程执行t1的。
t2线程执行t2的。
t1不会影响t2,t2也不会影响t1。这叫做真正的多线程并发。
单核的CPU表示只有一个大脑:
不能够做到真正的多线程并发,但是可以做到给人一种“多线程并发”的感觉。
对于单核的CPU来说,在某一个时间点上实际上只能处理一件事情,但是由于
CPU的处理速度极快,多个线程之间频繁切换执行,跟人来的感觉是:多个事情
同时在做!!!!!
线程A:播放音乐
线程B:运行魔兽游戏
线程A和线程B频繁切换执行,人类会感觉音乐一直在播放,游戏一直在运行,
给我们的感觉是同时并发的。
电影院采用胶卷播放电影,一个胶卷一个胶卷播放速度达到一定程度之后,
人类的眼睛产生了错觉,感觉是动画的。这说明人类的反应速度很慢,就像
一根钢针扎到手上,到最终感觉到疼,这个过程是需要“很长的”时间的,在
这个期间计算机可以进行亿万次的循环。所以计算机的执行速度很快。
5、java语言中,实现线程有两种方式,那两种方式呢?
java支持多线程机制。并且java已经将多线程实现了,我们只需要继承就行了。
第一种方式:编写一个类,直接继承java.lang.Thread,重写run方法。
// 定义线程类
public class MyThread extends Thread{
public void run(){
}
}
// 创建线程对象
MyThread t = new MyThread();
// 启动线程。
t.start();
第二种方式:编写一个类,实现java.lang.Runnable接口,实现run方法。
// 定义一个可运行的类
public class MyRunnable implements Runnable {
public void run(){
}
}
// 创建线程对象
Thread t = new Thread(new MyRunnable());
// 启动线程
t.start();
注意:第二种方式实现接口比较常用,因为一个类实现了接口,它还可以去继承
其它的类,更灵活。
6、关于线程对象的生命周期?
新建状态
就绪状态
运行状态
阻塞状态
死亡状态
1、(这部分内容属于了解)关于线程的调度
1.1、常见的线程调度模型有哪些?
抢占式调度模型:
那个线程的优先级比较高,抢到的CPU时间片的概率就高一些/多一些。
java采用的就是抢占式调度模型。
均分式调度模型:
平均分配CPU时间片。每个线程占有的CPU时间片时间长度一样。
平均分配,一切平等。
有一些编程语言,线程调度模型采用的是这种方式。
1.2、java中提供了哪些方法是和线程调度有关系的呢?
实例方法:
void setPriority(int newPriority) 设置线程的优先级
int getPriority() 获取线程优先级
最低优先级1
默认优先级是5
最高优先级10
优先级比较高的获取CPU时间片可能会多一些。(但也不完全是,大概率是多的。)
静态方法:
static void yield() 让位方法
暂停当前正在执行的线程对象,并执行其他线程
yield()方法不是阻塞方法。让当前线程让位,让给其它线程使用。
yield()方法的执行会让当前线程从“运行状态”回到“就绪状态”。
注意:在回到就绪之后,有可能还会再次抢到。
实例方法:
void join()
合并线程
class MyThread1 extends Thread {
public void doSome(){
MyThread2 t = new MyThread2();
t.join(); // 当前线程进入阻塞,t线程执行,直到t线程结束。当前线程才可以继续。
}
}
class MyThread2 extends Thread{
}
2、关于多线程并发环境下,数据的安全问题。
2.1、为什么这个是重点?
以后在开发中,我们的项目都是运行在服务器当中,
而服务器已经将线程的定义,线程对象的创建,线程
的启动等,都已经实现完了。这些代码我们都不需要
编写。
最重要的是:你要知道,你编写的程序需要放到一个
多线程的环境下运行,你更需要关注的是这些数据
在多线程并发的环境下是否是安全的。(重点:*****)
2.2、什么时候数据在多线程并发的环境下会存在安全问题呢?
三个条件:
条件1:多线程并发。
条件2:有共享数据。
条件3:共享数据有修改的行为。
满足以上3个条件之后,就会存在线程安全问题。
2.3、怎么解决线程安全问题呢?
当多线程并发的环境下,有共享数据,并且这个数据还会被修改,此时就存在
线程安全问题,怎么解决这个问题?
线程排队执行。(不能并发)。
用排队执行解决线程安全问题。
这种机制被称为:线程同步机制。
专业术语叫做:线程同步,实际上就是线程不能并发了,线程必须排队执行。
怎么解决线程安全问题呀?
使用“线程同步机制”。
线程同步就是线程排队了,线程排队了就会牺牲一部分效率,没办法,数据安全
第一位,只有数据安全了,我们才可以谈效率。数据不安全,没有效率的事儿。
2.4、说到线程同步这块,涉及到这两个专业术语:
异步编程模型:
线程t1和线程t2,各自执行各自的,t1不管t2,t2不管t1,
谁也不需要等谁,这种编程模型叫做:异步编程模型。
其实就是:多线程并发(效率较高。)
异步就是并发。
同步编程模型:
线程t1和线程t2,在线程t1执行的时候,必须等待t2线程执行
结束,或者说在t2线程执行的时候,必须等待t1线程执行结束,
两个线程之间发生了等待关系,这就是同步编程模型。
效率较低。线程排队执行。
同步就是排队。
3、Java中有三大变量?【重要的内容。】
实例变量:在堆中。
静态变量:在方法区。
局部变量:在栈中。
以上三大变量中:
局部变量永远都不会存在线程安全问题。
因为局部变量不共享。(一个线程一个栈。)
局部变量在栈中。所以局部变量永远都不会共享。
实例变量在堆中,堆只有1个。
静态变量在方法区中,方法区只有1个。
堆和方法区都是多线程共享的,所以可能存在线程安全问题。
局部变量+常量:不会有线程安全问题。
成员变量:可能会有线程安全问题。
4、如果使用局部变量的话:
建议使用:StringBuilder。
因为局部变量不存在线程安全问题。选择StringBuilder。
StringBuffer效率比较低。
ArrayList是非线程安全的。
Vector是线程安全的。
HashMap HashSet是非线程安全的。
Hashtable是线程安全的。
5、总结:
synchronized有三种写法:
第一种:同步代码块
灵活
synchronized(线程共享对象){
同步代码块;
}
第二种:在实例方法上使用synchronized
表示共享对象一定是this
并且同步代码块是整个方法体。
第三种:在静态方法上使用synchronized
表示找类锁。
类锁永远只有1把。
就算创建了100个对象,那类锁也只有一把。
对象锁:1个对象1把锁,100个对象100把锁。
类锁:100个对象,也可能只是1把类锁。
6、聊一聊,我们以后开发中应该怎么解决线程安全问题?
是一上来就选择线程同步吗?synchronized
不是,synchronized会让程序的执行效率降低,用户体验不好。
系统的用户吞吐量降低。用户体验差。在不得已的情况下再选择
线程同步机制。
第一种方案:尽量使用局部变量代替“实例变量和静态变量”。
第二种方案:如果必须是实例变量,那么可以考虑创建多个对象,这样
实例变量的内存就不共享了。(一个线程对应1个对象,100个线程对应100个对象,
对象不共享,就没有数据安全问题了。)
第三种方案:如果不能使用局部变量,对象也不能创建多个,这个时候
就只能选择synchronized了。线程同步机制。
7、线程这块还有那些内容呢?列举一下
7.1、守护线程
7.2、定时器
7.3、实现线程的第三种方式:FutureTask方式,实现Callable接口。(JDK8新特性。)
7.4、关于Object类中的wait和notify方法。(生产者和消费者模式!)
1、线程这块还有那些内容呢?列举一下
1.1、守护线程
java语言中线程分为两大类:
一类是:用户线程
一类是:守护线程(后台线程)
其中具有代表性的就是:垃圾回收线程(守护线程)。
守护线程的特点:
一般守护线程是一个死循环,所有的用户线程只要结束,
守护线程自动结束。
注意:主线程main方法是一个用户线程。
守护线程用在什么地方呢?
每天00:00的时候系统数据自动备份。
这个需要使用到定时器,并且我们可以将定时器设置为守护线程。
一直在那里看着,没到00:00的时候就备份一次。所有的用户线程
如果结束了,守护线程自动退出,没有必要进行数据备份了。
1.2、定时器
定时器的作用:
间隔特定的时间,执行特定的程序。
每周要进行银行账户的总账操作。
每天要进行数据的备份操作。
在实际的开发中,每隔多久执行一段特定的程序,这种需求是很常见的,
那么在java中其实可以采用多种方式实现:
可以使用sleep方法,睡眠,设置睡眠时间,没到这个时间点醒来,执行
任务。这种方式是最原始的定时器。(比较low)
在java的类库中已经写好了一个定时器:java.util.Timer,可以直接拿来用。
不过,这种方式在目前的开发中也很少用,因为现在有很多高级框架都是支持
定时任务的。
在实际的开发中,目前使用较多的是Spring框架中提供的SpringTask框架,
这个框架只要进行简单的配置,就可以完成定时器的任务。
1.3、实现线程的第三种方式:实现Callable接口。(JDK8新特性。)
这种方式实现的线程可以获取线程的返回值。
之前讲解的那两种方式是无法获取线程返回值的,因为run方法返回void。
思考:
系统委派一个线程去执行一个任务,该线程执行完任务之后,可能
会有一个执行结果,我们怎么能拿到这个执行结果呢?
使用第三种方式:实现Callable接口方式。
1.4、关于Object类中的wait和notify方法。(生产者和消费者模式!)
第一:wait和notify方法不是线程对象的方法,是java中任何一个java对象
都有的方法,因为这两个方式是Object类中自带的。
wait方法和notify方法不是通过线程对象调用,
不是这样的:t.wait(),也不是这样的:t.notify()..不对。
第二:wait()方法作用?
Object o = new Object();
o.wait();
表示:
让正在o对象上活动的线程进入等待状态,无期限等待,
直到被唤醒为止。
o.wait();方法的调用,会让“当前线程(正在o对象上
活动的线程)”进入等待状态。
第三:notify()方法作用?
Object o = new Object();
o.notify();
表示:
唤醒正在o对象上等待的线程。
还有一个notifyAll()方法:
这个方法是唤醒o对象上处于等待的所有线程。
2、反射机制
(比较简单,因为只要会查帮助文档,就可以了。)
2.1、反射机制有什么用?
通过java语言中的反射机制可以操作字节码文件。
优点类似于黑客。(可以读和修改字节码文件。)
通过反射机制可以操作代码片段。(class文件。)
2.2、反射机制的相关类在哪个包下?
java.lang.reflect.*;
2.3、反射机制相关的重要的类有哪些?
java.lang.Class:代表整个字节码,代表一个类型,代表整个类。
java.lang.reflect.Method:代表字节码中的方法字节码。代表类中的方法。
java.lang.reflect.Constructor:代表字节码中的构造方法字节码。代表类中的构造方法
java.lang.reflect.Field:代表字节码中的属性字节码。代表类中的成员变量(静态变量+实例变量)。
java.lang.Class:
public class User{
// Field
int no;
// Constructor
public User(){
}
public User(int no){
this.no = no;
}
// Method
public void setNo(int no){
this.no = no;
}
public int getNo(){
return no;
}
}
3、关于JDK中自带的类加载器:(聊一聊,不需要掌握,知道当然最好!)
3.1、什么是类加载器?
专门负责加载类的命令/工具。
ClassLoader
3.2、JDK中自带了3个类加载器
启动类加载器:rt.jar
扩展类加载器:ext/*.jar
应用类加载器:classpath
3.3、假设有这样一段代码:
String s = "abc";
代码在开始执行之前,会将所需要类全部加载到JVM当中。
通过类加载器加载,看到以上代码类加载器会找String.class
文件,找到就加载,那么是怎么进行加载的呢?
首先通过“启动类加载器”加载。
注意:启动类加载器专门加载:C:\Program Files\Java\jdk1.8.0_101\jre\lib\rt.jar
rt.jar中都是JDK最核心的类库。
如果通过“启动类加载器”加载不到的时候,
会通过"扩展类加载器"加载。
注意:扩展类加载器专门加载:C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\*.jar
如果“扩展类加载器”没有加载到,那么
会通过“应用类加载器”加载。
注意:应用类加载器专门加载:classpath中的类。
3.4、java中为了保证类加载的安全,使用了双亲委派机制。
优先从启动类加载器中加载,这个称为“父”
“父”无法加载到,再从扩展类加载器中加载,
这个称为“母”。双亲委派。如果都加载不到,
才会考虑从应用类加载器中加载。直到加载
到为止。
1、回顾反射机制
1.1、什么是反射机制?反射机制有什么用?
反射机制:可以操作字节码文件
作用:可以让程序更加灵活。
1.2、反射机制相关的类在哪个包下?
java.lang.reflect.*;
1.3、反射机制相关的主要的类?
java.lang.Class
java.lang.reflect.Method;
java.lang.reflect.Constructor;
java.lang.reflect.Field;
1.4、在java中获取Class的三种方式?
第一种:
Class c = Class.forName("完整类名");
第二种:
Class c = 对象.getClass();
第三种:
Class c = int.class;
Class c = String.class;
1.5、获取了Class之后,可以调用无参数构造方法来实例化对象
//c代表的就是日期Date类型
Class c = Class.forName("java.util.Date");
//实例化一个Date日期类型的对象
Object obj = c.newInstance();
一定要注意:
newInstance()底层调用的是该类型的无参数构造方法。
如果没有这个无参数构造方法会出现"实例化"异常。
1.6、如果你只想让一个类的“静态代码块”执行的话,你可以怎么做?
Class.forName("该类的类名");
这样类就加载,类加载的时候,静态代码块执行!!!!
在这里,对该方法的返回值不感兴趣,主要是为了使用“类加载”这个动作。
1.7、关于路径问题?
String path = Thread.currentThread().getContextClassLoader()
.getResource("写相对路径,但是这个相对路径从src出发开始找").getPath();
String path = Thread.currentThread().getContextClassLoader()
.getResource("abc").getPath(); //必须保证src下有abc文件。
String path = Thread.currentThread().getContextClassLoader()
.getResource("a/db").getPath(); //必须保证src下有a目录,a目录下有db文件。
String path = Thread.currentThread().getContextClassLoader()
.getResource("com/bjpowernode/test.properties").getPath();
//必须保证src下有com目录,com目录下有bjpowernode目录。
//bjpowernode目录下有test.properties文件。
这种方式是为了获取一个文件的绝对路径。(通用方式,不会受到环境移植的影响。)
但是该文件要求放在类路径下,换句话说:也就是放到src下面。
src下是类的根路径。
直接以流的形式返回:
InputStream in = Thread.currentThread().getContextClassLoader()
.getResourceAsStream("com/bjpowernode/test.properties");
1.8、IO + Properties,怎么快速绑定属性资源文件?
//要求:第一这个文件必须在类路径下
//第二这个文件必须是以.properties结尾。
ResourceBundle bundle = ResourceBundle.getBundle("com/bjpowernode/test");
String value = bundle.getString(key);
2、今日反射机制的重点内容
2.1、通过反射机制访问对象的某个属性。
2.2、通过反射机制调用对象的某个方法。
2.3、通过反射机制调用某个构造方法实例化对象。
2.4、通过反射机制获取父类以及父类型接口。