写在前面:此篇文章并不是完整的从零开始学Java,而是作者自身根据所学知识的简要回顾,因此并不适合想要从零入门的同学,但对于已经学习过Java的可以进来看看,当作简要的知识回顾。文章中列举的知识点只是作者仅存脑海中或者查阅资料而书写的学习笔记,是对自身学习的写照。也因为Java如此庞大的知识体系,故难以叙述地面面俱到,如果你有任何更好的意见,欢迎随时留言,鄙人将吸取精华而改进。望大家不吝赐教!
Hello,this is Reflect. He always confirm there is no one is fool and just some lazy people.
SO , Open The Door . And Let’s Go!!!
封装:即访问控制权限。
概述:成员变量隐藏在对象内部,外界无法直接操作。
封装原则:私有属性用private修饰隐藏在类内部,不允许外界程序直接访问,提供getter和setter方法(二者方法必须为public)供外界访问。
好处:通过方法操作成员变量,提高了代码的安全性;把代码用方法进行封装,提高了代码的复用性
继承:Java中只能单继承,但是支持多层继承(Grandpa->Father->Son)
多态:同一个对象,在不同时刻表现出来的不同形态
4类8种
类型 | 长度(位) | 占用存储空间(字节) | 默认值 | 取值范围 |
---|---|---|---|---|
byte | 8 | 1 | 0 | -272^7^-1,即-128127 |
short | 16 | 2 | 0 | -215~215-1,即-32 768~32 767 |
int | 32 | 4 | 0 | -231~231-1,即-2 147 483 648~2 147 483 647 |
long | 64 | 8 | 0L(不建议小写,容易与1混淆) | -263~263-1 |
float | 32 | 4 | 0.0f/F | 略 |
double | 64 | 8 | 0.0d | 略 |
char | 16 | 2 | \u0000 | |
boolean | 8 | 1 | false | true、false |
null:空值,引用数据类型的默认值,表示不指向任何有效对象。
懂的都懂。辨析两种自增,理解位运算符、与或非、三元运算符
i++:先赋值再自加
++i:先自加再赋值
条件?a:b:条件成立执行a,不成立执行b
循环语句
while、do while
for
执行次数可以提前确定
for (int i=0;i<10;i++){ System.out.println("执行"); } // ①执行初始语句 int i=0 // ②判断逻辑表达式 i<10 // ③执行语句或语句块 print // ④执行迭代语句 i++ // 第二次及其以后执行只需要②③④
分支语句:
跳转语句:
为了简化数组和 Collection集合的遍历
实现 Iterable接口的类允许其对象成为增强型for语句的目标
JDK5.0之后,引入了增强for循环,其内部原理是一个 Iterator迭代器。也叫做for each (loop)循环
注意:能被增强for迭代遍历的必须要是数组或集合类的派生子类。例如Map不能直接使用增强for
for (int i : arr) { System.out.println(i); }
验证其内部原理是一个Iterator迭代器
会抛出并发修改异常,与Iterator进行add抛出异常相同
for (String s:list) { if(s.equals("world")) { list.add("reflect"); } }
二者使用的时候没有区别,但是读的时候完全不同。推荐一般使用格式一
int[] arr:这种叫做定义了一个int类型的数组名叫arr
int arr[]:这种叫做定义了一个int类型的变量,变量名是arr数组
动态初始化:只需要在后面指定数组的长度,系统为之分配默认值(如int默认值为0)
int[] arr1 = new int[3];
静态初始化:需要给出指定的元素,但不需要指定数组的长度,系统会根据指定的元素个数自动推断长度
int[] arr2 = new int[]{1,2,3}; int[] arr3 = {1,2,3}; // 静态初始化的简化格式
使用索引访问数组,从0开始。数组的长度获取为arr.length
常见问题:
索引越界(ArrayIndexOutOfBoundsException
):即不能使用arr[arr.length]访问,数组的最后一位为arr[arr.length-1]
空指针异常(NullPointerException
):访问的数组已经不再执行堆内存的数据,造成空指针异常。简单示例如下:
int[] arr = new int[3]; System.out.println(arr[2]);// 输出0 arr = null; System.out.println(arr[0]);// 空指针异常
这个就。。二维数组类似于线性代数中的矩阵一样。多维数组也好理解,不过多解释了。
数组变量之间的复制实际是引用赋值,如下
int[] a = new int[6]; int[] b; b = a; System.out.println(a == b);// 将输出true,因为二者指向同一个引用
int[] a= {'a','b','c','d'}; // 此处实际存放的是'a','b,'c','d'的ASCII码 int[] c = new int[4]; System.arraycopy(a,0,c,0,4); System.out.println(a == c); for (int i : c) { System.out.println(i); // 输出97-100 }
public [static] [返回类型] MethodName([参数列表]) { // 方法体 return 数据; }
static决定是否为静态方法,定义为静态方法之后可直接使用MethodName调用方法;否则需要通过new了方法所在的类得到的对象**.**(点)MethodName调用
参数列表中的参数格式:数据类型+变量名(多个参数用逗号分隔)
返回类型:返回类型必须与return后面的返回值类型相同
数据:方法操作完成之后返回的数据类型与返回类型要一致
方法必须先定义后调用,否则将会找不到方法。
有参数的方法调用的时候必须传递与参数类型相同的变量或常量。
对于基本数据类型的参数,形参的改变,不影响实参的值(如int类型作为实参,实际操作只是将实参的值传递给形参,即使形参进行值的修改也并不会影响实参所引用的值)
对于引用类型的参数,形参的改变,影响实参的值(如数组作为实参,而形参使用arr[1]改变其中的值,实参中的arr[1]也发生改变)
类是对现实中一系列事物的抽象。
示例:
package com.reflect.pojo; /** * author: Administrator * date: 2020/12/30 - 10:08 */ public class User { private int id; private String name; private String pwd; public User() { } public User(int id, String name, String pwd) { this.id = id; this.name = name; this.pwd = pwd; } 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 String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", pwd='" + pwd + '\'' + '}'; } }
public class User // 此为示例,下面一行为语法格式。类名每个单词的首字母都大写,其余小写 [public][abstract][final] class ClassName [extends SuperClassName] [implements InterfaceNameList]
[public | protected | private][static][final][transient][volatile] type variableName;
方法中的变量即为局部变量,即其存在方法内或方法声明中
内存中的位置存在栈内存中。
随着方法的调用而存在,方法的使用完毕而消失
没有默认的初始值,必须先定义值才能使用
寻思着也用不着写代码吧,毕竟应该都知道吧。。。直接上理论了!
构造方法主要分为无参构造和有参构造。
构造方法可以重载,即有参构造可以不止一个。
比如上述的User类中,只写了一个全部参数的有参构造。但是如果需要的话,可以重载构造方法。其实是一种多态的体现。
方法重写是继承中的关系,但是与方法重载经常放在一起,因此放在一起做个分析。
方法重载
的条件:
// 方法重载示例 public User(int id) { this.id = id; } public User(String name) { this.name = name; } public User(int id, String name) { this.id = id; this.name = name; }
方法重写
的条件:
@Override
注解来标识重写的方法,可以帮助检测是否符合重写规范。this和super都是Java中的关键字。super是继承中使用的关键字,但是在此,放在一起做个区分。
this代表当前对象,可以调用方法、调用属性和指向对象本身。
一般有三种指向:
指向当前对象
/** * author: Administrator * date: 2021/1/3 - 13:54 */ public class Test1 { public static class Apple { int i = 0; Apple eatApple(){ i++; return this; } public static void main(String[] args) { Apple apple = new Apple(); // 链式编程 apple.eatApple().eatApple(); } } }
这段代码的精妙之处就在链式编程,竟然可以多次调用eatApple方法,实际上就是this关键字的效果,返回的一直都是Apple类自身,所以可以一直调用。
this修饰属性,最常见就是在构造方法中使用。几乎每个有参构造中都有用到。
与构造函数一起使用,充当全局关键字的效果
public class Apple { private int num; private String color; public Apple(int num){ this(num,"ᕁᜋ"); } public Apple(String color){ this(1,color); } public Apple(int num, String color) { this.num = num; this.color = color; } }
上面这段代码使用的不是this,而是this(参数)。相当于调用了其它构造方法,然后传递参数进去。
需要注意的是:this()必须放在构造方法的第一行,否则编译不通过
如果把this理解为指向自身的一个引用,那么super就是执行父类的一个引用。
关键字 | 调用方式 | 调用位置 | 调用次数 |
---|---|---|---|
this | 调用本类中的属性、构造函数、方法 | 构造函数第一行 | 一个构造函数只能调用一次 |
super | 调用父类中的属性、构造函数、方法 | 构造函数第一行 | 一个构造函数只能调用一次 |
4种不同的访问权限
private | default | protected | public | |
---|---|---|---|---|
同一个类 | √ | √ | √ | √ |
同一个包 | √ | √ | √ | |
子类 | √ | √ | ||
其他包中的类 | √ |
final关键字是最终的意思,可以修饰成员方法、成员变量、类
final修饰的特点
数据值
不能发生改变地址值
不能发生改变,但是地址里面的内容是可以发生改变的static关键字是静态的意思,可以修饰成员方法,成员变量
static修饰的特点
推荐使用类名调用
。静态代码块
不属于任何方法体并且以static关键字修饰的语句块,称为静态语句块。常用来进行类变量的初始化。
static { ... }
static访问特点
一句话:静态成员方法只能访问静态成员(不能访问非静态的;而非静态成员方法可以访问非静态或者静态)
其实对应就是文件夹,作用对类进行分类管理
package 包名(多级包使用*.*分开,包名统一使用小写)
使用其它包下的类时,需要书写类的全路径,写起来较麻烦,为了简化此操作,Java提供了导包功能(import)
我们都知道Car car = new Car();
就可以new出一个对象car。但是需要知道在new出car中进行了类的初始化。
上述的new中括号没有参数,实际上就是使用了无参构造。
Java会尽量
(难免出现异常)保证每个变量在使用前都会获得初始化。初始化涉及两种成员初始化。
int a = 11
。通过构造器可以初始化其中的参数值
System.gc()
方法执行垃圾回收作用域决定了其内部定义的变量名的可见性和生命周期。通常由{}来决定。
1.8中String的两个重要属性(JDK1.8及其之前的底层原理字符串方法char[])
1.9中String的三个重要属性(JDK1.9及其之后的底层原理字符串方法byte[])
目的为了节约内存,但是也只有当内容全为拉丁字符(英文字母)时才能节省。
1.8及其以前默认拼接方式为StringBuilder
1.9及其以后默认拼接方式为invokedynamic,若还行继续使用StringBuilder为拼接方式,可在javac时加参数
String的内容是不可变的,字符串拼接时容易浪费空间
package com.reflect.base.string; /** * author: Administrator * date: 2021/1/3 - 8:42 */ /** * 字符串的六种基本的创建方式 * 使用char[]数组配合new来创建,JDK1.8及其之前的底层原理字符串方法 * 1.8及其之前版本,不管任何类型创建之后会转换为char[],为了统一字符集编码为Unicode * 使用byte[]数组配合new来创建,JDK1.9及其之后的底层原理字符串方法,目的是为了更节约内存。并且增加了invokedynamic指令扩展字符串的拼接实现方式 * 使用int[]数组配合new来创建 * 使用已有字符串配合new来创建 * 使用字面量创建(不使用new),最熟悉的创建方式。三点: * 非对象:字面量在代码运行到它所在语句之前,它还不是字符串对象 * 懒加载:当第一次用到"abc"字面量时,才会创建对应的字符串的对象 * 不重复:同一个类中,值相同的字面量只有一份。再次创建相同的值时,其实是引用之前的地址 * 合二为一,使用+运算符来拼接创建 */ public class StringEstablishTest { public static void main(String[] args) { // 字符串的六种基本的创建方式 // 使用char[]数组配合new来创建,JDK1.8及其之前的底层原理字符串方法 String s1 = new String(new char[]{'a','b','c'}); // 使用byte[]数组配合new来创建,JDK1.9及其之后的底层原理字符串方法,以及网络传递(http)和I/O读取的数据 String s2 = new String(new byte[]{97, 98, 99}); // 使用int[]数组配合new来创建,unicode中的emoji表情就是如此存储的 String s3 = new String(new int[]{0x1F602}, 0, 1); // 使用已有字符串配合new来创建,这种方式最为简单,但是s4与s1引用的是同一个char[]对象 String s4 = new String(s1); // 使用字面量创建(不使用new) String s5 = "abc"; // 合二为一,使用+运算符来拼接创建 // 真正没有[拼接]操作发生,从源码编译为字节码时,javac就已经把'a'和'b'串在一起,这是一种编译期的优化处理 String s6 = "a" +"b"; // 编译器发现x为final已经不可变,相当于x直接可以被'b'替换,最后如上所示编译 final String x = "b"; String s7 = "a" + x; // x为变量,不能在编译期间确定值,运行期间可能发生改变, // 所以要真正进行拼接操作——创建StringBuilder进行拼接(append()之后再toString返回成字符串) String y = "b"; String s8 = "a" + y; // 1为int类型,也需要使用StringBuilder进行拼接操作 String s9 = "a" + 1; } }
基本类型比较的是数据值是否相同;
引用类型比较的是地址值是否相同。
String不属于基本数据类型,所以上述比较的引用地址,如果想要比较数据值是否相同,则使用equals()比较内容
public boolean equals(Object anObject)
:将此字符串与指定对象进行比较。
public char charAt(int index)
获取字符串中的每一个字符
使用s+=“abc”
倒序charAt遍历,然后再使用+拼接
可变的字符串类(即StringBuilder中的内容是可变的),可以看成是一个容器。
String与StringBuilder的转换
StringBuilder sb = new StringBuilder(); sb.append("hello"); // String s =sb; // 错误的做法 String s = sb.toString(); System.out.println(s); String s = "hello"; // StringBuilder sb = s; // 错误的做法 StringBuilder sb = new StringBuilder(s); System.out.println(sb);
采用sb.append()方法。
new StringBuilder(s).reverse().toString()
只有通过字面量创建的String才会进入StringTable管理,好处就是其中的值不会重复,减少内存开销。
StringTable本质其实是哈希表,字符串对象就是hash表中的key,key的不重复性,是hash表的基本特性。
@Test public void test1() { String s1 = "abc"; // StringTable管理 String s2 = "abc"; // 同上 String s3 = new String(new char[]{'a','b','c'}); // 不受StringTable管理 String s4 = "a" + "bc"; // 由于前后都是定值,所以在编译期间会直接创建"abc",受到StringTable管理 String x = "a"; // x为变量 String s5 = x + "bc"; // 由于x为变量,所以编译期间不会直接创建"abc",因此不收到StringTable管理 System.out.println(s1 == s2); // true System.out.println(s1 == s3); // false System.out.println(s1 == s4); // true System.out.println(s1 == s5); // false }
字符串提供了intern方法实现去重,让其它字符串有机会
受到StringTable管理。
public native String intern();
此方法会尝试将调用者放入StringTable
String x = ...; String s = x.intern();
如果已经存在,则会返回其中已经存在的String对象
@Test public void test2() { String x = new String(new char[]{'a','b','c'}); // 不受管理 String y = "abc"; // 受管理 String z = x.intern(); // 已经存在,返回StringTable中的"abc",即y System.out.println(z == x); // false System.out.println(z == y); // true }
如果StringTable中不存在,则将x的引用对象加入StringTable,返回StringTable中的对象
如果StringTable中不存在,则将x的引用对象复制一份,将新复制的那份加入StringTable,返回StringTable中的对象
StringTable属于方法区,方法区的实现叫作永久代(PerGen)
方法区变成了元空间(Metaspace),1.7开始StringTable不属于方法区,而是从方法区移到了堆(heap)中。
需要手动调用才能实现去重。
使用JDK8u220,可使用JVM参数开启G1垃圾回收器,并开启字符串去重功能
-XX:+UseG1GC -XX:+UseStringDeduplication
原理是让多个字符串对象引用同一个char[]来达到节省内存的目的
StringTable足够大,才能发挥性能优势。意味着String在hash表中冲突减少,链表短,性能高。
JDK8中默认大小为60013,StringTable底层的hash表在JVM启动后大小就固定不变了。
当字符串没有引用时,会收垃圾回收掉!
继承中访问特点:说白了可以这样理解,就是自己力所能及的事情就不用就麻烦长辈,否则就需要向长辈请教,如果长辈也不懂,那么就报错。
在子类中访问一个变量的顺序
在与this关键字的时候已经说过,不过多叙述,知道这也是一个重点即可。
1.子类中所有的构造方法默认都会访问父类中的无参构造方法
每个子类的构造方法的第一条语句默认都是super()
,这就是即使调用子类的有参构造创建时,父类初始化还是使用无参构造的原因2.如果父类中没有无参构造,只有带参构造,如何进行初始化?
通过子类对象访问一个方法
上面已经与方法重载做了区分条件,此处只讲解在继承中的注意事项。
我们可以说猫是猫,也可以说猫是动物。
这里猫在不同的时刻表现出不同的形态,这就是多态。
Cat cat = new Cat(); Animal animal = new Cat();
为什么成员变量和成员方法的访问不一样?
转型之前为了防止出错,一般要先测试确定对象的类型,然后再执行转换。
// 判断a是否是Cat类型 Animal a = new Cat(); if(a instanceof Cat) { // dosomething }
从子到父,父类引用指向子类对象
Animal a = new Cat(); // 向上转型 a.eat(); // Animal中和猫都有此方法 // a.playGame(); // Animal中没有此方法,猫中特有
从父到子,父类引用转为子类对象。即所谓的强制转型
向下转型,解决了不能访问子类中特有功能的弊端。
Cat c = (Cat) a; // 将上面的Animal对象a向下转型为Cat对象c c.eat(); // 猫中方法 c.playGame(); // 猫中方法
Java中,一个没有方法体
的方法应该定义为抽象方法
,而类中如果有抽象方法
,该类必须定义为抽象类
。
就是一个方法没有{}
这个方法体,就要变成抽象方法,而一个类中只要有抽象方法就要成为抽象类,但是其中也可以包含非抽象方法。递推的过程应该容易理解吧。
没有{} ---> 此方法为抽象方法 ---> 此类为抽象类 但是 抽象类 --×->类中方法都为抽象方法
抽象类和抽象方法必须使用abstract关键字修饰
抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
抽象类不能实例化
如果要实例化呢?参照多态的方式,通过子类对象实例化,这叫抽象类多态
Animal a = new Cat(); // 抽象类Animal,Cat为其子类,通过此种方式实例化
抽象类的子类
所有抽象方法
Java中的接口更多的体现在对行为的抽象
继承类,但是要实现(implements)接口
在一个类中定义一个类,也称为嵌套类。
public class Outer{ 修饰符 class Inner{ } }
根据内部类在类中定义的位置不同,分为两种形式
public class Outer{ public class Inner{ ... } }
// 当内部类的修饰符为public时,可使用如下格式进行创建内部类对象 Outer.Inner oi = new Outer().new Inner(); // 但是内部类的定义就是为了使得内部类隐藏起来,不能被外界调用。 // 所以,内部类的修饰符一般为private,此时将不能再通过如上方式创建内部类对象使用
public class Outer{ private class Inner{ ... } public void method() { Inner i = new Inner(); } }
// Outer.Inner oi = new Outer().new Inner(); // 失效 // 此时需要使用内部类时可通过创建Outer对象,使用其method方法对内部类进行操控即可
public class Outer{ public void method() { class Inner{ ... } // 若method方法中存在局部变量,Inner类可以直接访问method的变量 // Inner i = new Inner(); // 需要使用局部内部类时创建出此对象 } }
// 直接通过Outer的对象调用method的方法访问不到局部内部类,因为在里面没有任何关于内部类的创建对象 Outer o = new Outer(); o.method(); // 如果上述的局部内部类一行不存在,将无法对Inner进行操作。写上之后才可进行操作
匿名内部类是局部内部类的特殊形式。
前提:存在一个类或接口,这里的类可以是具体类或抽象类
格式:
new Inter() { // 此处的Inter为类名或接口名,中间show为重写的方法 public void show() { ... } }; // 此处不要忘记分号
本质:是一个继承了该类或该接口的子类匿名对象
即上述代码可以在前面用Inter i =
去承接这个对象,之后直接使用i.show()
即可
public class Outer{ public static class Inner{ ... } }
Math包含执行基本数字运算的方法
没有构造方法,如何使用类中的成员?
方法名 | 说明 |
---|---|
public static int abs(int a) | 返回参数的绝对值 |
public static double ceil(double a) | 返回大于或等于参数的最小double值,等于一个整数 |
public static double floor(double a) | 返回小于或等于参数的最大double值,等于一个整数 |
public static int round(float a) | 按照四舍五入返回最接近参数的int |
public static int max(int a,int b) | 返回两个int值中的较大值 |
public static int min(int a,int b) | 返回两个int值中的较小值 |
public static double pow(double a,double b) | 返回a的b次幂的值 |
public staticdouble random() | 返回值为double的正值,[0.0,1.0) |
方法名 | 说明 |
---|---|
public static void exit(int status) | 终止当前运行的Java虚拟机,非零表示异常终止 |
public static long currentTimeMills() | 返回当前时间(以毫秒为单位) |
Object是Java中类层次数的根。每个类都是Object的直接或间接子类。正是由于这种特殊地位,此类中定义了所有对象都需要的状态和行为。
在Object子类中可以重写以下Object类的方法。
其中,clone是复制,hashCode是根据哈希算法计算此对象的哈希码,equals比较内容是否相等,finalize为对象终结的方法,toString对象的字符串表示,以及最后三个是与线程相关的方法。
引用也是相同的
但引用不同
public boolean equals (object obj)
equals比较当前对象的引用是否与参数obj执行同一个对象,如果指向同一个对象返回true。但String、Date、File类和所有包装类(Integer、Long等)都重写此方法,改为比较所指向对象的内容。str1.equals(str2)
,而不是使用"=="Arrays类包含用于操作数组的各种方法
方法名 | 说明 |
---|---|
public static String toString(int[] a) | 返回指定数组的内容的字符串表示形式 |
public static void sort(int[] a) | 按照数字顺序排列指定的数组 |
将基本数据类型封装成对象的好处在于可以在对应中定义更多的功能方法操作该数据
常用操作之一:用于基本数据类型和字符串之间的转换
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
方法名 | 说明 |
---|---|
public Integer(int value) | 根据int值创建Integer对象(过时) |
public Integer(String s) | 根据String值创建Integer对象(过时) |
public static Integer valueOf(int i) | 返回一个标识指定的int值的Integer实例 |
public static Integer valueOf(String s) | 返回一个保存指定值的Integer对象String |
public static valueOf(int i)
:返回int参数的字符串表示形式。该方法是String类中的方法public static int parseInt(String s)
:将字符串解析为int类型,该方法是Integer类中的方法package com.reflect.base.box; /** * author: Administrator * date: 2021/1/4 - 15:16 */ public class AutoBox { public static void main(String[] args) { // 装箱:把基本数据类型转换为对应的包装类类型 Integer i1 = Integer.valueOf(100); // 装箱操作 Integer i2 = 100; // 自动装箱,JDK1.5做出的改进,底层也使用了Integer.valueOf() // 拆箱:把包装类类型转换为对应的基本数据类型 i1 = i1.intValue() + 200; // 先进行拆箱成int类型,再与int类型的200相加 i2 = i2 + 200; //自动拆箱,JDK1.5做出的改进,底层也使用了i2.intValue()拆箱,再进行自动装箱 Integer i = null; // i += 300; // NullPointerException // 鉴于以上操作,在对包装类型进行使用时都先进行判断是否为空操作 if(i != null) { i += 300; } } }
注意:在使用包装类类型的时候,如果做操作,最好先判断是否为null
推荐的是,只要是对象,在使用前就必须进行不为null的判断
方法名 | 说明 |
---|---|
public long getTime() | 获取的日期对象从1970年1月1日00:00:00到现在的毫秒值 |
public void setTime(long time) | 设置时间,给的是毫秒值 |
SimpleDateFormat是一个具体的类,用于以区域设置敏感的方式格式化和解析日期。重点掌握日期格式化和解析
日期和时间格式由日期和时间模式字符串指定,在提起和时间模式中,从’A’到’Z’以及从’a’到’z’引导的字母被解释为表示日期和时间字符串的组件的模式字母
常用模式和字母对应关系
方法名 | 说明 |
---|---|
public SimpleDateFormat() | 构造一个SimpleDateFormat,使用默认模式和日期格式 |
public SimpleDateFormat(String pattern) | 构造一个SimpleDateFormat使用给定的模式和默认的日期格式 |
Calendar为某一时刻和一组日历字段之间的转换提供了一些方法,并为操作日历字段提供了一些方法
Calendar提供了一个类方法getInstance用于获取Calendar对象,其日历字段已使用当前日期和时间初始化
Calendat rightNow = Calendar.getInstance();
package com.reflect.base.calendar; import java.util.Calendar; /** * author: Administrator * date: 2021/1/4 - 15:55 */ public class CalendarTest { public static void main(String[] args) { // 获取日历类对象 Calendar instance = Calendar.getInstance(); // 返回给定日历字段的值 int year = instance.get(Calendar.YEAR); int month = instance.get(Calendar.MONTH) + 1; // 此处的日历从0开始,因此要加1 int date = instance.get(Calendar.DATE); System.out.println(year +"年"+ month +"月"+ date +"日"); } }
方法名 | 说明 |
---|---|
public int get(int field) | 返回给定日历字段的值 |
public abstract void add(int field,int amount) | 根据日历的规则,将指定的时间量添加或减去给定的日历字段 |
public final void set(int year,int month,int date) | 设置当前日历的年月日 |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yJaQqpC2-1618663004183)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210104162526813.png)]
如果程序出现了问题,我们不做任何处理,最终JVM会做默认处理
格式
try { 可能出现异常的代码; } catch(异常类名 变量名) { 异常的处理代码; }
执行流程
方法名 | 说明 |
---|---|
public String getMessage() | 返回此throwable的详细信息字符串 |
public String toString() | 返回此可抛出的简短描述 |
public void printStackTrace() | 把异常的错误信息输出在控制台 |
printStackTrace输出的信息最全面,一般使用此方法
Java中的异常分为两大类:编译时异常
和运行时异常
,也被成为受检异常
和非受检异常
所有的RuntimeException类及其子类被称为运行时异常,其它的异常都是编译时异常。
虽然try…catch…可以对异常进行处理,但并不是所有情况都有权限进行异常的处理。
当有些异常处理不了,Java提供了throws的处理方案
throws实际上只是声明异常,throw实际上是直接抛出异常
throws 异常类名; // 注意:这个格式要跟在方法的括号后
throws:实际上只是声明异常,并不处理
方法声明后
,跟的是异常类名
;throw:实际上的抛出异常
方法体内
,跟的是异常对象名
// throw抛出异常示例 if (条件) { throw new RuntimeException("输出结果"); }
public class 异常类名 extends Exception { 无参构造 有参构造 }
泛型:是JDK5中引入的特性,它提供了编译时类型安全检测机制,该机制允许在编译时检测到非法的类型
它的本质是参数化类型
,也就是说所操作的数据类型被指定为一个参数
一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传弟实参。那么参数化类型怎么理解呢?
顾名思义,就是将类型由原来的具体的类型参数化,然后在使用/调用时传入具体的类型
这种参数类型可以用在类、方法和接口中,分别被称为泛型类、泛型方法、泛型接口
把运行时期的问题提前到了编译期间
避免了强制类型转换
泛型类的定义格式
泛型方法的定义格式:
泛型接口的定义格式:
格式:修饰符 interface接口名<类型>{}
范例:public interface Generic(}
为了表示各种泛型List的父类,可以使用类型通配符
<?>
任何的类型
如果说我们不希望List<?>是任何泛型List的父类,只希望它代表某一类泛型Lis的父类,可以使用类型通配符的上限
<? extends类型>
Number或者其子类型
除了可以指定类型通配符的上限,我们也可以指定类型通配符的下限
<? super类型>
Number或者其父类型
可变参数又称参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的了
可变参数要放在最后
Arrays工具类中有一个静态方法
List接口中有一个静态方法
Set接口中有一个静态方法:
boolean add(E e):添加元素
boolean remove(Object o):从集合中移除指定的元素
void clear():清空集合中的元素
boolean contains(Object o):判断集合中是否存在指定的元素
boolean isEmpty():判断集合是否为空
int size():集合的长度,也就是集合中元素的个数
Iterator:迭代器,集合的专用遍历方式
Iterator中的常用方法
栈是一种数据先进后出的模型。
一端开口(栈顶),一段封闭(栈底)
队列是一种数据先进先出的模型。
一端开口(队尾),一端开口(队首)
数组是一种查询快,增删慢的模型
链表是一种增删快,查询慢的模型(对比数组而言)
数组+链表
实现,可以说是一个元素为链表的数组有序:存储和取出的元素顺序一致
可重复:存储的元素可以重复
void add(int index, E element):在此集合中的指定位置插入指定的元素
E remove(int index):删除指定索引处的元素,返回被删除的元素
E set(int index, E element):修改指定索引处的元素,返回被修改的元素
E get(int index):返回指定索引处的元素
ListIterator中的常用方法
ArrayList:底层数据结构是数组,查询快,增删慢
LinkedList:底层数据结构是链表,查询慢,增删快
public ArrayList():创建一个空的集合对象
public boolean add(Ee):将指定的元素追加到此集合的末尾
public void add(int index,E element):在此集合中的指定位置插入指定的元素
public void addFirst(Ee):在该列表开头插入指定的元素
void addLast(Ee):将指定的元素追加到此列表的末尾
public E getFirst():返回此列表中的第一个元素
public E getLast():返回此列表中的最后个元素
public E removeFirst():从此列表中删除并返回第一个元素
public E removeLast():从此列表中删除并返回最后一个元素
哈希值:是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
Object类中有一个方法可以获取对象的哈希值
public static void main(String[] args) { // 默认情况,hashCode是不相同的。这是因为String中重写了hashCode方法 System.out.println("重地".hashCode()); // 1179395 System.out.println("通话".hashCode()); // 1179395 }
在比较的对象类中实现此接口
自然排序
对元素进行排序的让元素所属的类实现 Comparable接口
,重写 compareTo(T o)方法在TreeSet构造方法中传递一个比较器实现此接口
创建Map集合的对象
转换为Map集合中的操作:
Map<String, String> map = new HashMap<>(); map.put("1","10"); map.put("2","20"); map.put("3","30"); // 获取所有键的集合,用keySet()方法实现 Set<String> keySet = map.keySet(); for (String key : keySet) { String value = map.get(key); System.out.println(key + "," + value); }
转换为Map集合中的操作
// 获取所有键值对对象的集合,用entrySet()方法实现 Set<Map.Entry<String, String>> entrySet = map.entrySet(); for (Map.Entry<String, String> entry : entrySet) { String key = entry.getKey(); String value = entry.getValue(); System.out.println(key + "," +value); }
public static< T extends Comparable<? super T>> void sort(List list):将指定的列表按升序排序
public static void reverse(List<?>list):反转指定列表中元素的顺序
public static void shuffle(List<?>list:使用默认的随机源机排列指定的列表
File:它是文件和目录路径名的抽象表示
File(String pathname):通过将给定的路径名字符串转换为抽象路径名来创建新的File实例
File(String parent, String child):从父路径名字符串和子路径名字符串创建新的File实例
File(File parent, String child):从父抽象路径名和子路径名字符串创建新的File实例
绝对路径和相对路径的区别
删除目录时的注意事项:
递归概述:以编程的角度来看,递归指的是方法定义中调用方法本身的现象
递归解决问题的思路:
把一个复杂的问题层层转化为个与原问题相似的模较小
的问题来求解
递归策略只需少量的程序
就可描述出解题过程所需要的多次重复计算
递归解决问题要找到两个内容:
一般来说,我们说IO流的分类是按照数据类型
来分的
那么这两种流都在什么情况下使用呢?
字节流
FileOutputStream:文件输出流用于将数据写入File
使用字节输出流写数据的步骤:
完整示例
FileOutputStream fos = null; try { fos = new FileOutputStream("Z:\\reflect\\java.txt"); fos.write("hello".getBytes()); } catch (IOException e) { e.printStackTrace(); } finally { // 如果此处不进行判断,当上述路径错误时,此处将会发生空指针异常 if (fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } }
FileInputStream:从文件系统中的文件获取输入字节
使用字节输入流读数据的步骤:
FileInputStream fis = new FileInputStream(".\\fos.txt"); int by; while ((by = fis.read()) != -1) { System.out.print((char)by); } fis.close();
FileInputStream fis = new FileInputStream(".\\fos.txt"); byte[] bytes = new byte[1024]; // 一般都是1024及其整数倍 int len; while ((len = fis.read(bytes)) != -1) { System.out.print(new String(bytes,0,len)); } fis.close();
构造方法
为什么构造方法需要的是字节流,而不是具体的文件或者路径呢?
仅仅提供缓冲区
,而真正的读写数据还得依靠基本的字节流对象进行操作由于字节流操作中文不是特别的方便,所以Java就提供字符流
用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文,如何识别是中文的呢
第一个字节都是负数
基础知识
二进制
数表示的;我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果编码
。反之,将存储在计算杋中的进制数按照某种规则解析显示出来,称为解码
。这里强调—下:按照八A编码存储,必须按照A编码解析,这样才能显示正确的文本符号。否则就会导致乱码现象字符编码:就是一套自然语言的字符与二进制数之间的对应规则(A–>65)Unicode字符集
UTF-8
编码:可以用来表示 Unicode标准中任意字符,它是电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用一至四个字节为每个字符编码小结:采用何种规则编码,就要采用对应规则解码,否则就会出现乱码
编码
解码
字符流抽象基类
字符流中和编码解码问题相关的两个类:
flush():刷新流,还可以继续写数据
close():关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据
int read():一次读一个字符数据
int read(char[ ] cbuf):一次读一个字符数组数据
BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接受默认大小。默认值足够大,可用于大多数用途
BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小。默认值足够大,可用于大多数用途
构造方法:
Bufferedwriter
Bufferedreader
// 读取输入 BufferedReader br = new BufferedReader(new InputStream(System.in)); // 输出 // PrintStream类的字节流方式即为System.out // PrintWriter可以使得程序更加国际化 PrintWriter pw = new PrintWriter(System.out, true);
Systen类中有两个静态的成员变量
自己实现键盘录入数据:
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
写起来太麻烦,Java就提供了一个类实现键盘录入
Scanner sc = new Scanner(Sytem.in);
输出语句的本质:是一个标准的输出流
PrintStream ps = new System.out;
PrintStream类有的方法,System.out都可以使用
打印流分类
打印流的特点:
PrintStream(String fileName):使用指定的文件名创建新的打印流
使用继承父类的方法写数据,查看的时候会转码;使用自己的特有方法写数据,查看的数据原样输出
PrintWriter的构造方法:
对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象
这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息
字节序列写到文件之后,相当于文件中持久保存了一个对象的信息
反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化
要实现序列化和反序列化就要使用对象序列化流和对象反序列化流
构造方法
序列化对象的方法
注意
一个对象要想被序列化,该对象所属的类必须必须实现Serializable
接囗
Serializable是—个标记接口
,实现该接口,不需要重写任何方法
对象反序列化流:ObjectInputStream
构造方法
反序列化对象的方法
用对象序列化流序列化了一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出问题呢?
InvalidClassException
异常如果出问题了,如何解决呢?
给对象所属的类加一个serialVersionUID
private static final long serialVersionUID= 42L;
如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
transient
关键字修饰,该关键字标记的成员变量不参与序列化过程进程:是正在运行的程序
线程:是进程中的单个顺序控制流,是一条执行路径
方式1:继承 Thread类
方式2:实现 Runnable接口
多线程的实现方案有两种
相比继承 Thread类,实现 Runnable接口的好处
两个小问题
Thread类中设置和获取线程名称的方法
如何获取main()方法所在的线程名称?
线程有两种调度模型
Java使用的是抢占式调度模型
假如计算机只有一个CPU,那么CPU在某一个时刻只能执行一条指令,线程只有得到CPU时间片,也就是使用权,才可以执行指令。所以说多线程程序的执行是有随机性
,因为抢到CPU的使用权是不一定的
Thread类中设置和获取线程优先级的方法
5
,优先级范围是1-10
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GPKR4AEg-1618663004188)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210114000705783.png)]
锁多条语句操作共享数据,可以使用同步代码块实现
格式
synchronized(任意对象){
多条语句操作共享数据的代码
}
synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁
同步的好处和弊端
同步方法:就是把 synchronized关键字加到方法上
格式
修饰符 synchronized 返回值类型 方法名(方法参数){ }
同步方法的锁对象是什么呢?
同步静态方法:就是把 synchronized关键字加到静态方法上
格式
修饰符 static synchronized 返回值类型 方法名(方法参数){ }
同步静态方法的锁对象是什么呢?
线程安全的类
StringBuffer
StringBuilder
替代。通常应该使用 StringBuilder类,因为它支持所有相同的操作,但它更快,因为它不执行同步Vector
Hashtable
Hashtable
被同步。如果不需要线程安全的实现,建议使用HashMap代替HashtableStringBuffer可能会用到,但是后两个基本用不到,被如下代替了。
List<String> list = Collections.synchronizedList(new ArrayList<String>()); // 返回线程安全的List Set<String> set = Collections.synchronizedSet(new HashSet<String>()); // 返回线程安全的Set Map<String> map = Collections.synchronizedMap(new HashMap<String>()); // 返回线程安全的Map
虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的对象Lock
Lock实现提供比使用 synchronized方法和语句可以获得更广泛的锁定操作
Lock中提供了获得锁和释放锁的方法
Lock是接口不能直接实例化,这里采用它的实现类 ReentrantLock来实例化
ReentrantLock的构造方法
生产者消费者模式概述
为了体现生产和消费过程中的等待和唤醒,Java就提供了几个方法供我们使用,这几个方法在 Object类中
Object类的等待和唤醒方法
IP地址
端口
协议
为了方便我们对P地址的获取和操作,Java提供了一个类InetAddress供我们使用
InetAddress:此类表示Internet协议(IP)地址
UDP协议
用户数据报协议(User Datagram Protocol)
UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。
由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输。
TCP协议
传输控制协议(Transmission Control Protocol)
TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据它提供了两台计算机之间可靠无差错的数据传输。在ICP娑中必须要明确客户端与服务器端,由客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手“
三次握手:TCP协议中,在发送数据的准佳备阶段,客户端与服务器之间的三次交互,以保证连接的可靠
完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性, TCP协议可以保证传输数据的安全,所以应用十分广泛。例如上传文件、下载文件、浏览网页等。
TCP通信协议是一种可靠的网络协议,它在通信的两端各建立一个 Socket对象,从而在通信的两端形成网络虚拟链路一旦建立了虛拟的网络链路,两端的程序就可以通过虛拟链路进行通信
Java对基于TCP协议的的网络提供了良好的封装,使用 Socket象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信
Java为客户端提供了Socket类,为服务器端提供了ServerSocket类
在数学中,函数就是有输入量输出量的套计算方案,也就是“拿数据做操作
需求:启动一个线程,输出一句话。
// 匿名内部类的方式 new Thread(new Runnable() { @Override public void run() { System.out.println("线程启动了!!!"); } }).start(); // Lambda表达式方式改进 new Thread( () -> { System.out.println("线程启动了!!!"); }).start();
组成Lambda表达式的三要素:形式参数
、箭头
、代码块
。
(形式参数)-> 代码块
->
:由英文中画线和大于符号组成,固定写法。代表指向动作使用前提:
省略规则:
注意事项
局部变量的赋值
得知 Lambda对应的接口:Runnable r = ()-> System.out.printIn(" Lambda表达式");调用方法的参数
得知 Lambda对应的接口:new Thread(()-> System.out.println(“Lambda表达式”)).start();所需类型不同
使用限制不同
不能使用 Lambda表达式
实现原理不同
接口的组成
常量:public static final
抽象方法:public abstract
默认方法(Java8)
静态方法(Java8)
私有方法(Java9)
default
返回值类型方法名(参数列表){}default
void show()(}接口中默认方法的注意事项
:
static
返回值类型方法名(参数列表){}static
void show(){}接口中静态方法的注意事项:
Java9中新増了带方法体的私有方法,这其实在Java8中就埋下了伏笔:Java8允许在接口中定义带方法体的默认方法和静态方法。这样可能就会引发一个问题:当两个默认方法或者静态方法中包含段相同的代码实现时,程序必然考虑将这段实现代码抽取成一个共性方法,而这个共性方法是不需要让别人使用的,因此用私有给隐藏起来,这就是Java9增加私有方法的必然性
接口中私有方法的定义格式
接口中私有方法的注意事项
方法引用符
∷
该符号为引用运算符,而它所在的表达式被称为方法引用回顾一下体验方法引用中的代码
::
printin);
推导与省略
常见的引用方式
引用类方法,其实就是引用类的静态方法
类名::静态方法
Integer::parseInt
Lambda表达式被类方法代替时,它的形参将全部传递给静态方法作参数
引用对象的实例方法,其实就引用对象中的成员方法
对象::成员方法
"Hello World"::toUpperCase
Lambda表达式被对象的实例方法代替时,它的形参将全部传递给静态方法作参数
引用类的实例方法,其实就是引用类中的成员方法
类名::成员方法
String::substring
Lambda表达式被类的实例方法代替时,第一个参数作为调用者
,后面的参数全部传递给该方法作为参数
引用构造器,其实就是引用构造方法
格式:类名::new
范例:Student::new
Lambda表达式被构造器代替时,它的形参将全部传递给静态方法作参数
函数式接口:有且仅有一个抽象方法的接口
Java中的函数式编程体现就是 Lambda表达式,所以函数式接口就是可以适用于 Lambda使用的接口
只有确保接口中有且仅有一个抽象方法,Java中的 Lambda才能利地进行推导
如何检测一个接口是不是函数式接口呢?
@Functionallnterface
放在接口定义的上方:如果接口是函数式接口,编译通过;如果不是,编译失败
注意
如果方法的参数是一个函数式接口,我们可以使用 Lambda表达式作为参数传递
如果方法的返回值是一个函数式接口,我们可以使用 Lambda表达式作为结果返回
private static Comparator<String> getComparator {
return(s1, s2) -> s1.length() - s2.length();
}
Java8在 java.util.function包下预定义了大量的数式接口供使用
重点来学习下面的4个接囗
Supplier:包含一个无参的方法
Consumer:包含两个方法
Predicate:常用的四个方法
Function<T,R>:常用的两个方法
Stream流的常见生成方式
// Collection体系的集合可以使用默认方法 stream()生成流 // List集合生成流 List<String> list = new ArrayList<>(); Stream<String> listStream = list.stream(); // Set集合生成流 Set<String> set = new HashSet<>(); Stream<String> setStream = set.stream(); // Map体系的集合间接的生成流 Map<String,Integer> map = new HashMap<>(); // Key的流生成 Stream<String> keyStream = map.keySet().stream(); // Value的流生成 Stream<Integer> valueStream = map.values().stream(); // Key-Value对的流生成 Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream(); // 数组可以通过 Stream接口的静态方法of(T… values)生成流 String[] strArray = {"hello","world","java"}; Stream<String> strArrayStream1 = Stream.of(strArray); Stream<String> strArrayStream2 = Stream.of("hello", "world", "java"); Stream<Integer> intStream = Stream.of(10, 20, 30);
对数据使用 Strean流的方式操作完毕后,想把流中的数据收集到集合中,该怎么办呢?
Stream流的收集方法
工具类 Collectors提供了具体的收集方式
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载、类的连接、类的初始化这三个步骤来对类进行初始化。如果不出意外的情况,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或类初始化
注意:在执行第2个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3
Bootstrap class loader:它是虚拟机的内置类加载器,通常表示为null,并且没有父null
Platform class loader:平台类加载器可以看到所有平台类,平台类包括由平台类加载器或其祖先定义的 Java Se平台AP其实现类和JDK特定的运行时类
System class loader:它也被称为应用程序类加载器,与平台类加载器不同。系统类加载器通常用于定义应用程序类路径,模块路径和JK特定工具上的类
类加载器的继承关系:System的父加载器为 Platform,而 Platform的父加载器为 Bootstrap
Java反射机制:是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的种机制。由于这种动态性,可以极大的増强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展
我们要想通过反射去使用一个类,首先我们要获取到该类的字节码文件对象,也就是类型为Class类型的对象
这里提供三种方式获取Class类型的对象
package com.reflect.base.reflect; /** * author: Administrator * date: 2021/1/4 - 17:46 */ import com.reflect.base.test.Test1; /** * - 使用类的class属性来获取该类对应的class对象。举例:Student.class将会返回 Student类对应的class对象 * - 调用对象的 getClass()方法,返回该对象所属类对应的Class对象 * - 使用Class类中的静态方法forName(String className)该方法需要传入字符串参数,该字符串参数的值是某个类的全路径,也就是完整包名的路径 */ public class ReflectTest { public static void main(String[] args) throws ClassNotFoundException { // 使用类的class属性来获取该类对应的class对象 Class<Test1> c1 = Test1.class; System.out.println(c1); Class<Test1> c2 = Test1.class; System.out.println(c2); System.out.println(c1 == c2); // 调用对象的 getClass()方法,返回该对象所属类对应的Class对象 Test1 test1 = new Test1(); Class<? extends Test1> c3 = test1.getClass(); System.out.println(c1 == c3); System.out.println("=========="); // 使用Class类中的静态方法forName(String className)该方法需要传入字符串参数,该字符串参数的值是某个类的全路径,也就是完整包名的路径 Class<?> c4 = Class.forName("com.reflect.base.test.Test1"); System.out.println(c1 == c4); } }
Class类中用于获取构造方法的方法
<T>
getConstructor(Class<?>… parameterTypes):返回单个公共构造方法对象<T>
getDeclaredConstructor(class<?> parameter Types):返回单个构造方法对象Constructor类中用于创建对象的方法
值得一提
Class类中用于获取成员变量的方法
Field类中用于给成员变量赋值的方法
Class类中用于获取成员方法的方法
Method类中用于调用成员方法的方法
package com.reflect.base.reflect; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; /** * author: Administrator * date: 2021/1/11 - 0:22 */ public class ReflectDemo3 { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { // 创建集合 ArrayList<Integer> arrayList = new ArrayList<>(); // // 正常添加元素 // arrayList.add(1); // arrayList.add(2); // arrayList.add(3); // // 添加字符串将会报错,泛型检查 // arrayList.add("reflect"); Class<? extends ArrayList> aClass = arrayList.getClass(); Method m = aClass.getMethod("add", Object.class); m.invoke(arrayList,"hello"); m.invoke(arrayList,"world"); m.invoke(arrayList,"reflect"); System.out.println(arrayList); } }