=============================================================================
=============================================================================
涉及到的知识点有:
1:反射(理解)
(1)类的加载
(2)类的初始化时机
(3)类加载器
(4)类加载器的组成
(5)反射的概述
(6)反射的使用
(7)反射的案例
(8)动态代理(中介)
(9)Java中的代理类Proxy和调用处理接口InvocationHandler
(10)代理类Proxy中的方法创建动态代理类对象
(11)调用处理接口InvocationHandler的方法
(12)动态代理案例
2:设计模式
A:模版设计模式(抽象类中用的多)
B:装饰设计模式(IO流中用的多)
C:适配器模式(GUI中用的多)
3:JDK新特性
(1)JDK5(掌握)
(2)JDK6(很少见,了解)
(3)JDK7(理解)
(4)JDK8(了解)
=============================================================================
=============================================================================
1:反射(理解)
(1)类的加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载、连接、初始化三步来实现对这个类进行初始化。
加载
就是指将class文件读入内存,并为之创建一个Class对象。
任何类被使用时系统都会建立一个Class对象。
连接
验证:是否有正确的内部结构,并和其他类协调一致。
准备:负责为类的静态成员分配内存,并设置默认初始化值。
解析:将类的二进制数据中的符号引用替换为直接引用。
初始化
就是我们以前讲过的初始化步骤。
注意:Object类的方法:
public final Class getClass() 返回对象的字节码文件对象
Class类的方法:
public String getName() 以 String 的形式返回此 Class 对象所表示的实体名称。(实体包括:类、接口、数组名、基本类型或 void)
即:可以通过Class类中的一个方法,获取对象的真实类的全名称。
--------------------------------------
(2)类的初始化时机
1.创建类的实例时。
2.访问类的静态变量,或者为静态变量赋值时。
3.调用类的静态方法时。
4.使用反射方式来强制创建某个类或接口对应的java.lang.Class对象时。
5.初始化某个类的子类时。
6.直接使用java.exe命令来运行某个主类时。
--------------------------------------
(3)类加载器
负责将.class文件加载到内在中,并为之生成对应的Class对象。
虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。
(4)类加载器的组成
Bootstrap ClassLoader 根类加载器
Extension ClassLoader 扩展类加载器
Sysetm ClassLoader 系统类加载器
Bootstrap ClassLoader 根类加载器
也被称为引导类加载器,负责Java核心类的加载。
比如System类,String类等。在JDK中JRE的lib目录下rt.jar文件中(JDK8以前版本中的位置,JDK9/10位置变化了)。
Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载。
在JDK中JRE的lib目录下ext目录。
Sysetm ClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。
一般我们自己写的类通过系统类加载器来加载的。
如果我们仅仅站在这些class文件的角度,我们如何来使用这些class文件中的内容呢?
--------------------------------------
(5)反射的概述
JAVA反射机制是在运行状态中,
对于任意一个类,都能够知道这个类的所有属性和方法(动态获取信息);
对于任意一个对象,都能够调用它的任意一个方法和属性(动态调用对象的方法);
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
简言之:通过字节码文件对象,去使用该文件中的成员变量、构造方法、成员方法。
要想解剖一个类,必须先要获取到该类的字节码文件对象。
而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象。
获取class文件对象的方式:
A:Object类的getClass()方法
B:数据类型的静态属性class(任意数据类型都具备一个class静态属性)
C:Class类中的静态方法(将类名作为字符串传递给Class类中的静态方法forName)
public static Class forName(String className)
1:Person p = new Person();
Class c = p.getClass();
2:Class c2 = Person.class;
任意数据类型都具备一个class静态属性,看上去要比第一种方式简单。
3:将类名作为字符串传递给Class类中的静态方法forName()即可。
注意:需要类的全路径(带包名的路径)
Class c3 = Class.forName("Person");
4:第三种和前两种的区别
前两种你必须明确Person类型。
第三种需要这种类型的字符串就行(开发中用)。
这种扩展更强,不需要知道具体的类,只提供字符串,按照配置文件加载就可以了。
示例代码如下:


1 package cn.itcast_01;
2
3 public class Person {
4 private String name;
5 int age;
6 public String address;
7
8 public Person() {
9 }
10
11 private Person(String name) {
12 this.name = name;
13 }
14
15 Person(String name, int age) {
16 this.name = name;
17 this.age = age;
18 }
19
20 public Person(String name, int age, String address) {
21 this.name = name;
22 this.age = age;
23 this.address = address;
24 }
25
26 public void show() {
27 System.out.println("show");
28 }
29
30 public void method(String s) {
31 System.out.println("method " + s);
32 }
33
34 public String getString(String s, int i) {
35 return s + "---" + i;
36 }
37
38 private void function() {
39 System.out.println("function");
40 }
41
42 @Override
43 public String toString() {
44 return "Person [name=" + name + ", age=" + age + ", address=" + address + "]";
45 }
46
47 }
Person.java


1 package cn.itcast_01;
2
3 /*
4 * 反射:就是通过class文件对象,去使用该文件中的成员变量,构造方法,成员方法。
5 *
6 * Person p = new Person();
7 * p.使用;
8 *
9 * 要想这样使用,首先你必须得到class文件对象,其实也就是得到Class类的对象。
10 * .class文件 --> Class类
11 * 成员变量 --> Field类
12 * 构造方法 --> Constructor类
13 * 成员方法 --> Method类
14 *
15 * 获取class文件对象的方式:
16 * A:Object类的getClass()方法
17 * B:数据类型的静态属性class(任意数据类型都具备一个class静态属性)
18 * C:Class类中的静态方法(将类名作为字符串传递给Class类中的静态方法forName)
19 * public static Class forName(String className)
20 *
21 * 一般我们到底使用谁呢?
22 * A:自己玩 任选一种,第二种比较方便
23 * B:开发时 第三种
24 * 为什么呢?因为第三种是一个字符串,而不是一个具体的类名。这样我们就可以把这样的字符串配置到配置文件中。
25 */
26 public class ReflectDemo {
27 public static void main(String[] args) throws ClassNotFoundException {
28 // 方式A
29 Person p = new Person();
30 Class c = p.getClass();
31
32 Person p2 = new Person();
33 Class c2 = p2.getClass();
34
35 System.out.println(p == p2); // false
36 System.out.println(c == c2); // true
37
38 // 方式B
39 Class c3 = Person.class;
40 // int.class;
41 // String.class;
42 System.out.println(c == c3); // true
43
44 // 方式C
45 // ClassNotFoundException 需要类的全路径(带包名的路径)
46 Class c4 = Class.forName("cn.itcast_01.Person");
47 System.out.println(c == c4); // true
48 }
49 }
ReflectDemo.java
--------------------------------------
(6)反射的使用
// 获取字节码文件对象
Class c = Class.forName("cn.itcast_01.Person");
A:通过反射获取构造方法并使用
Constructor con = c.getConstructor(String.class, ...); // 获取单个的公共构造方法
Constructor[] cons = c.getConstructors(); // 获取所有的公共构造方法
Constructor con = c.getDeclaredConstructor(String.class, ...); // 获取单个的构造方法
Constructor[] cons = c.getDeclaredConstructors(); // 获取所有的构造方法


1 package cn.itcast_02;
2
3 import java.lang.reflect.Constructor;
4
5 //import cn.itcast_01.Person;
6
7 /*
8 * 需求:通过反射去获取该公共无参构造方法并使用:
9 * public Person() {
10 * }
11 *
12 * 以前的做法:
13 * Person p = new Person();
14 * System.out.println(p);
15 *
16 * 现在的做法如下:
17 *
18 * 反射的特点:
19 * 1.Class类中的静态方法forName()传入的字符串将来会做成配置信息文件,所以以后你不知道程序运行的是谁(是哪个类)。
20 * 2.反射是不会看到类的任何信息的。即通过构造方法对象Constructor、成员方法对象Method,调用他们的方法返回值都是Object类型。
21 * (因为任何类型都可以用Object类型接收,基本数据类型会自动装箱为引用数据类型)。
22 * 3.反射可以访问私有的东西(前提是class文件未被加密)。
23 */
24 public class ReflectDemo {
25 public static void main(String[] args) throws Exception {
26 // 获取字节码文件对象
27 // Class类的静态方法:public static Class forName(String className)
28 Class c = Class.forName("cn.itcast_01.Person");
29
30 // 获取构造方法
31 // Class类的成员方法:public Constructor[] getConstructors() 获取所有的公共构造方法
32 // Class类的成员方法:public Constructor[] getDeclaredConstructors() 获取所有的构造方法
33 // Constructor[] cons = c.getDeclaredConstructors();
34 // for (Constructor con : cons) {
35 // System.out.println(con);
36 // }
37
38 // Class类的成员方法:public Constructor<T> getConstructor(Class<?>... parameterTypes) 获取单个的公共构造方法
39 // Class类的成员方法:public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 获取单个的构造方法
40 // 参数表示的是:要获取的构造方法的构造参数个数和数据类型的字节码文件对象
41 // 通过字节码对象获取公共无参构造方法对象
42 Constructor con = c.getConstructor(); // 返回的是构造方法的对象
43
44 // Person p = new Person();
45 // System.out.println(p);
46 // Constructor类的成员方法:public T newInstance(Object... initargs) 使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例
47 // 通过公共无参构造方法对象创建一个实例对象
48 Object obj = con.newInstance();
49 System.out.println(obj); // Person [name=null, age=0, address=null]
50
51 // Person p = (Person)obj;
52 // p.show();
53 }
54 }
通过反射去获取公共无参构造方法并使用


1 package cn.itcast_02;
2
3 import java.lang.reflect.Constructor;
4
5 /*
6 * 需求:通过反射去获取该公共带参构造方法并使用:
7 * public Person(String name, int age, String address) {
8 * this.name = name;
9 * this.age = age;
10 * this.address = address;
11 * }
12 *
13 * 以前的做法:
14 * Person p = new Person("林青霞", 27, "北京");
15 * System.out.println(p);
16 *
17 * 现在的做法如下:
18 */
19 public class ReflectDemo2 {
20 public static void main(String[] args) throws Exception {
21 // 获取字节码文件对象
22 // Class类的静态方法:public static Class forName(String className)
23 Class c = Class.forName("cn.itcast_01.Person");
24
25 // 通过字节码对象获取公共带参构造方法对象
26 // Class类的成员方法:public Constructor<T> getConstructor(Class<?>... parameterTypes)
27 Constructor con = c.getConstructor(String.class, int.class, String.class);
28
29 // 通过公共带参构造方法对象创建一个实例对象
30 // Constructor类的成员方法:public T newInstance(Object... initargs)
31 Object obj = con.newInstance("林青霞", 27, "北京");
32
33 System.out.println(obj);
34 }
35 }
通过反射去获取公共带参构造方法并使用


1 package cn.itcast_02;
2
3 import java.lang.reflect.Constructor;
4
5 /*
6 * 需求:通过反射获取私有带参构造方法并使用
7 * private Person(String name) {
8 * this.name = name;
9 * }
10 *
11 * 以前的做法:
12 * Person p = new Person("风清扬");
13 * System.out.println(p);
14 *
15 * 现在的做法如下:
16 *
17 * 可以访问私有构造方法了,但是呢?是不是就不安全了。
18 * 不用担心,我们可以有以下两种方式解决:
19 * 1.将class文件加密,这样就还原不出来了。
20 * 2.对某个数据或者字符使用位异或,这样也就还原不出来了。
21 */
22 public class ReflectDemo3 {
23 public static void main(String[] args) throws Exception {
24 // 获取字节码文件对象
25 // Class类的静态方法:public static Class forName(String className)
26 Class c = Class.forName("cn.itcast_01.Person");
27
28 // 通过字节码对象获取私有带参构造方法对象
29 // NoSuchMethodException 没有这个方法异常
30 // 原因是:一开始我们使用的方法只能获取公有的构造方法,下面这种方式就可以了。即获取所有的构造方法。
31 Constructor con = c.getDeclaredConstructor(String.class);
32
33 // 通过私有带参构造方法对象创建一个实例对象
34 // IllegalAccessException 非法的访问异常
35 // 肿么办? 暴力访问
36 con.setAccessible(true); // 值为true,则指示反射的对象在使用时应该取消Java语言的访问检查
37 Object obj = con.newInstance("风清扬");
38
39 System.out.println(obj);
40 }
41 }
通过反射去获取私有带参构造方法并使用
B:通过反射获取成员变量并使用
Field field = c.getField("address"); // 获取单个的公共成员变量
Field[] fields = c.getFields(); // 获取所有的公共成员变量
Field field = c.getDeclaredField("name"); // 获取单个的成员变量
Field[] fields = c.getDeclaredFields(); // 获取所有的成员变量


1 package cn.itcast_03;
2
3 import java.lang.reflect.Constructor;
4 import java.lang.reflect.Field;
5
6 /*
7 * 需求:通过反射获取成员变量并使用
8 *
9 */
10 public class ReflectDemo {
11 public static void main(String[] args) throws Exception {
12 // 获取字节码文件对象
13 Class c = Class.forName("cn.itcast_01.Person");
14
15 // Field field = c.getField("address"); // 获取单个的公共成员变量
16 // Field field = c.getDeclaredField("name"); // 获取单个的成员变量
17 // Field[] fields = c.getFields(); // 获取所有的公共成员变量
18 // Field[] fields = c.getDeclaredFields(); // 获取所有的成员变量
19
20 // for (Field field : fields) {
21 // System.out.println(field);
22 // }
23
24 /*
25 * Person p = new Person();
26 * p.address = "北京";
27 * System.out.println(p);
28 */
29
30 // 通过字节码对象获取公共无参构造方法对象
31 Constructor con = c.getConstructor();
32
33 // 通过公共无参构造方法对象创建一个实例对象
34 Object obj = con.newInstance();
35 System.out.println(obj);
36
37 // 通过字节码对象获取成员变量对象
38
39 // 获取单个的公共成员变量address,并对其赋值
40 Field addressField = c.getField("address");
41 // Field类的成员方法:public void set(Object obj, Object value) 将指定对象变量上此 Field对象表示的字段设置为指定的新值
42 addressField.set(obj, "北京"); // 给obj对象的addressField字段设置值为"北京"
43 System.out.println(obj);
44
45 // 获取成员变量name,并对其赋值
46 // NoSuchFieldException 没有这个方法异常
47 Field nameField = c.getDeclaredField("name");
48 // IllegalAccessException 非法的访问异常
49 // 肿么办? 暴力访问
50 nameField.setAccessible(true);
51 nameField.set(obj, "林青霞");
52 System.out.println(obj);
53
54 // 获取成员变量age,并对其赋值
55 Field ageField = c.getDeclaredField("age");
56 ageField.setAccessible(true);
57 ageField.set(obj, 27);
58 System.out.println(obj);
59 }
60 }
通过反射获取成员变量并使用
C:通过反射获取成员方法并使用
Method method = c.getMethod("show", String.class, ...); // 获取自己单个的公共方法
Method[] methods = c.getMethods(); // 获取自己和父类所有的公共方法
Method method = c.getDeclaredMethod("function", String.class, ...); // 获取单个的方法
Method[] methods = c.getDeclaredMethods(); // 获取自己所有的方法


1 package cn.itcast_04;
2
3 import java.lang.reflect.Constructor;
4 import java.lang.reflect.Method;
5
6 /*
7 * 需求:通过反射获取成员方法并使用
8 *
9 */
10 public class ReflectDemo {
11 public static void main(String[] args) throws Exception {
12 // 获取字节码文件对象
13 Class c = Class.forName("cn.itcast_01.Person");
14
15 // Method method = c.getMethod("show", String.class, ...); // 获取自己单个的公共方法
16 // Method method = c.getDeclaredMethod("function", String.class, ...); // 获取单个的方法
17 // Method[] methods = c.getMethods(); // 获取自己和父类所有的公共方法
18 // Method[] methods = c.getDeclaredMethods(); // 获取自己所有的方法
19
20 // for (Method method : methods) {
21 // System.out.println(method);
22 // }
23
24 Constructor con = c.getConstructor();
25 Object obj = con.newInstance();
26
27 /*
28 * Person p = new Person();
29 * p.show();
30 */
31
32 // Class类的成员方法:public Method getMethod(String name, Class<?>... parameterTypes) 第一个参数表示的方法名,第二个参数表示的是方法的参数的class类型
33 // Method类的方法:public Object invoke(Object obj, Object... args) 返回值是Object类型接收,第一个参数表示对象是谁,第二参数表示调用该方法的实际参数
34
35 // 获取单个方法并使用
36 // 获取Person类的成员方法:public void show()
37 Method m1 = c.getMethod("show");
38 m1.invoke(obj); // 本质是:调用obj对象的m1方法,而obj对象是由Person对象得来的
39
40 // 获取Person类的成员方法:public void method(String s)
41 Method m2 = c.getMethod("method", String.class);
42 m2.invoke(obj, "hello");
43
44 // 获取Person类的成员方法:public String getString(String s, int i)
45 Method m3 = c.getMethod("getString", String.class, int.class);
46 Object objString = m3.invoke(obj, "hello", 100);
47 System.out.println(objString);
48 // String s = (String) m3.invoke(obj, "hello",100);
49 // System.out.println(s);
50
51 // 获取Person类的成员方法:private void function()
52 Method m4 = c.getDeclaredMethod("function");
53 m4.setAccessible(true);
54 m4.invoke(obj);
55 }
56 }
通过反射获取成员方法并使用
反射的特点:
1.Class类中的静态方法forName()传入的字符串将来会做成配置信息文件,所以以后你不知道程序运行的是谁(是哪个类)。
2.反射是不会看到类的任何信息的。即通过构造方法对象Constructor、成员方法对象Method,调用他们的方法返回值都是Object类型。
(因为任何类型都可以用Object类型接收,基本数据类型会自动装箱为引用数据类型)。
3.反射可以访问私有的东西(前提是class文件未被加密)。
--------------------------------------
(7)反射的案例
A:通过反射运行配置文件中的内容
(即通过配置文件运行类中的方法)


1 package cn.itcast.test;
2
3 public class Student {
4 public void love() {
5 System.out.println("爱生活,爱Java");
6 }
7 }
Student.java


1 package cn.itcast.test;
2
3 public class Teacher {
4 public void love() {
5 System.out.println("爱生活,爱青霞");
6 }
7 }
Teacher.java


1 package cn.itcast.test;
2
3 public class Worker {
4 public void love() {
5 System.out.println("爱生活,爱老婆");
6 }
7 }
Worker.java


1 className=cn.itcast.test.Worker
2 methodName=love
配置文件.txt


1 package cn.itcast.test;
2
3 import java.io.FileReader;
4 import java.lang.reflect.Constructor;
5 import java.lang.reflect.Method;
6 import java.util.Properties;
7
8 /*
9 * 通过反射运行配置文件中的内容
10 * (通过配置文件运行类中的方法)
11 *
12 * 反射的做法:
13 * 需要有配置文件配合使用。
14 * 假如我们用class.txt代替。
15 * 并且你要知道有两个键(即你得知道键:因为键相同,值覆盖)。
16 * className
17 * methodName
18 *
19 * 反射的作用:
20 * 一旦反射的代码写定后,就不修改它了。
21 * 将来只需要提供配置文件,根据配置文件就可以动态的知道执行的是谁。即反射的动态执行效果。
22 *
23 * 因为只要反射的代码不执行,我就不知道执行的是谁。
24 * 只有在代码的执行中,去动态的加载配置文件里面的东西。
25 * 所以通过反射写的代码,代码的灵活性高很多。
26 *
27 * 将来就业班学框架,用的都是反射的原理。
28 */
29 public class Test {
30 public static void main(String[] args) throws Exception {
31 // 反射前的做法(硬编码:代码写死了,写固定了,修改麻烦)
32 // Student s = new Student();
33 // s.love();
34 // Teacher t = new Teacher();
35 // t.love();
36 // Worker w = new Worker();
37 // w.love();
38
39 // 反射后的做法(软编码:代码写活了,灵活度高)
40 // 把文件中的数据加载到集合Properties中(即加载键值对数据)
41 Properties prop = new Properties();
42 FileReader fr = new FileReader("src//cn//itcast//test//class.txt");
43 prop.load(fr);
44 fr.close();
45
46 // 从集合Properties中获取数据,根据键获取值
47 String className = prop.getProperty("className");
48 String methodName = prop.getProperty("methodName");
49
50 // 反射
51 // 获取字节码文件对象
52 Class c = Class.forName(className);
53
54 Constructor con = c.getConstructor();
55 Object obj = con.newInstance();
56
57 // 调用方法
58 Method m = c.getMethod(methodName);
59 m.invoke(obj);
60 }
61 }
通过反射运行配置文件中的内容
B:通过反射越过泛型检查
(即我给你ArrayList<Integer>的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢?)


1 package cn.itcast.test;
2
3 import java.lang.reflect.InvocationTargetException;
4 import java.lang.reflect.Method;
5 import java.util.ArrayList;
6
7 /*
8 * 通过反射越过泛型检查
9 * (我给你ArrayList<Integer>的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢?)
10 */
11 public class ArrayListDemo {
12 public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException,
13 IllegalArgumentException, InvocationTargetException {
14
15 // 创建集合对象
16 ArrayList<Integer> array = new ArrayList<Integer>();
17
18 // 反射前的做法
19 // array.add("hello"); // 报错
20 // array.add(10); // 自动装箱,没问题
21
22 // 其实在集合的源码里面,ArrayList集合的add(E e)方法默认的数据类型是Object类型
23 // 只不过在JDK5以后,为了数据的安全,加入了泛型的机制,
24 // 而该泛型机制仅仅是给编译器看的(可以通过反编译工具查看哦),真正运行加载的class文件里面放的依旧是Object类型
25
26 // 反射后的做法
27 Class c = array.getClass(); // 集合ArrayList的class文件对象
28 Method m = c.getMethod("add", Object.class);
29
30 m.invoke(array, "hello"); // 调用array的add方法,传入的值是hello
31 m.invoke(array, "world");
32 m.invoke(array, "java");
33
34 System.out.println(array);
35 }
36 }
通过反射越过泛型检查
C:通过反射给任意的一个对象的任意的属性赋值为指定的值
(即写一个方法,public void setProperty(Object obj, String propertyName, Object value) {},此方法可将obj对象中名为propertyName的属性的值设置为value)


1 package cn.itcast.test;
2
3 import java.lang.reflect.Field;
4
5 /*
6 * 通过反射给任意的一个对象的任意的属性赋值为指定的值
7 * 写一个方法,public void setProperty(Object obj, String propertyName, Object value) {},
8 * 此方法可将obj对象中名为propertyName的属性的值设置为value。
9 */
10 public class Tool {
11 public void setProperty(Object obj, String propertyName, Object value)
12 throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
13
14 // 根据对象获取字节码文件对象
15 Class c = obj.getClass();
16 // 获取该对象的propertyName成员变量
17 Field field = c.getDeclaredField(propertyName);
18 // 取消访问检查
19 field.setAccessible(true);
20 // 给对象的成员变量赋值为指定的值
21 field.set(obj, value);
22 }
23
24 }
Tool.java


1 package cn.itcast.test;
2
3 /*
4 * 通过反射给任意的一个对象的任意的属性赋值为指定的值
5 * 写一个方法,public void setProperty(Object obj, String propertyName, Object value) {},
6 * 此方法可将obj对象中名为propertyName的属性的值设置为value。
7 */
8 public class ToolDemo {
9 public static void main(String[] args)
10 throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
11
12 Person p = new Person();
13 // p.name = "林青霞"; // 错误,因为name是私有成员变量
14 // p.age = 27; // 没问题,因为age是公共成员变量
15
16 Tool t = new Tool();
17 t.setProperty(p, "name", "林青霞");
18 t.setProperty(p, "age", 27);
19 System.out.println(p);
20 System.out.println("-----------");
21
22 Dog d = new Dog();
23 t.setProperty(d, "sex", '男');
24 t.setProperty(d, "price", 12.34f);
25 System.out.println(d);
26 }
27 }
28
29 class Person {
30 private String name;
31 public int age;
32
33 @Override
34 public String toString() {
35 return name + "---" + age;
36 }
37 }
38
39 class Dog {
40 char sex;
41 float price;
42
43 @Override
44 public String toString() {
45 return sex + "---" + price;
46 }
47 }
通过反射给任意的一个对象的任意的属性赋值为指定的值
(8)动态代理(中介)
代理:本来应该自己做的事情,却请了别人来做,被请的人就是代理对象。
举例:春季回家买票让人代买
动态代理:在程序运行过程中产生的这个代理对象。
而程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理。
(9)Java中的代理类Proxy和调用处理接口InvocationHandler
在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。
JDK提供的代理只能针对接口做代理。
我们有更强大的代理cglib(在学框架的时候用到)。
(10)代理类Proxy中的方法创建动态代理类对象
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
最终会调用InvocationHandler的方法。
(11)调用处理接口InvocationHandler的方法
Object invoke(Object proxy, Method method, Object[] args)
Proxy类中创建动态代理对象的方法的三个参数;
ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载。
Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了。
InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上。
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,
当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的invoke方法来进行调用。
InvocationHandler接口中invoke方法的三个参数:
proxy:代表动态代理对象。
method:代表正在执行的方法。
args:代表调用目标方法时传入的实参。
(12)动态代理案例
A:模拟给方法加权限校验和日志记录。


1 package cn.itcast_06;
2
3 /*
4 * 用户操作接口
5 */
6 public interface UserDao {
7 public abstract void add();
8
9 public abstract void delete();
10
11 public abstract void update();
12
13 public abstract void find();
14 }
UserDao.java


1 package cn.itcast_06;
2
3 public class UserDaoImpl implements UserDao {
4
5 @Override
6 public void add() {
7 System.out.println("添加功能");
8 }
9
10 @Override
11 public void delete() {
12 System.out.println("删除功能");
13 }
14
15 @Override
16 public void update() {
17 System.out.println("修改功能");
18 }
19
20 @Override
21 public void find() {
22 System.out.println("查找功能");
23 }
24
25 }
UserDaoImpl.java


1 package cn.itcast_06;
2
3 public interface StudentDao {
4 public abstract void login();
5
6 public abstract void regist();
7 }
StudentDao.java


1 package cn.itcast_06;
2
3 public class StudentDaoImpl implements StudentDao {
4
5 @Override
6 public void login() {
7 System.out.println("登录功能");
8 }
9
10 @Override
11 public void regist() {
12 System.out.println("注册功能");
13 }
14
15 }
StudentDaoImpl.java


1 package cn.itcast_06;
2
3 import java.lang.reflect.InvocationHandler;
4 import java.lang.reflect.Method;
5
6 public class MyInvocationHandler implements InvocationHandler {
7 private Object target; // 目标对象
8
9 public MyInvocationHandler(Object target) {
10 this.target = target;
11 }
12
13 @Override
14 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
15 System.out.println("权限校验");
16 Object result = method.invoke(target, args);
17 System.out.println("日志记录");
18 return result; // 返回的是代理对象
19 }
20
21 }
MyInvocationHandler.java


1 package cn.itcast_06;
2
3 import java.lang.reflect.Proxy;
4
5 public class Test {
6 public static void main(String[] args) {
7 // 基本的用户操作
8 UserDao ud = new UserDaoImpl();
9 ud.add();
10 ud.delete();
11 ud.update();
12 ud.find();
13 System.out.println("-----------");
14
15 // 我们要创建一个动态代理对象
16 // Proxy类中有一个静态方法可以创建动态代理对象
17 // public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
18 // 给目标对象ud创建一个代理对象
19 MyInvocationHandler handler = new MyInvocationHandler(ud);
20 UserDao proxy = (UserDao) Proxy.newProxyInstance(ud.getClass().getClassLoader(), ud.getClass().getInterfaces(), handler);
21 proxy.add();
22 proxy.delete();
23 proxy.update();
24 proxy.find();
25 System.out.println("-----------");
26
27 StudentDao sd = new StudentDaoImpl();
28 MyInvocationHandler handler2 = new MyInvocationHandler(sd);
29 StudentDao proxy2 = (StudentDao) Proxy.newProxyInstance(sd.getClass().getClassLoader(), sd.getClass().getInterfaces(), handler2);
30 proxy2.login();
31 proxy2.regist();
32 }
33
34 }
Test.java
B:用动态代理实现:计算集合ArrayList的add()方法的运行时间。
-----------------------------------------------------------------------------
2:设计模式
A:模版设计模式(抽象类中用的多)
模版设计模式的概述
模版方法模式就是定义一个算法的骨架,而将具体的算法延迟到子类中来实现。
优点
使用模版方法模式,在定义算法骨架的同时,可以很灵活的实现具体的算法,满足用户灵活多变的需求。
缺点
如果算法骨架有修改的话,则需要修改抽象类。
示例代码如下:


1 package cn.itcast_01;
2
3 import java.io.BufferedInputStream;
4 import java.io.BufferedOutputStream;
5 import java.io.FileInputStream;
6 import java.io.FileOutputStream;
7 import java.io.IOException;
8
9 /*
10 * 需求:请给计算出一段代码的运行时间
11 // for循环的运行时间
12 for (int x = 0; x < 10000; x++) {
13 System.out.println(x);
14 }
15
16 // 复制视频的运行时间
17 try {
18 BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src//cn//itcast_01//a.avi"));
19 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src//cn//itcast_01//b.avi"));
20 byte[] bys = new byte[1024];
21 int len = 0;
22 while ((len = bis.read(bys)) != -1) {
23 bos.write(bys, 0, len);
24 }
25 bos.close();
26 bis.close();
27 } catch (IOException e) {
28 e.printStackTrace();
29 }
30
31 // 再给我测试一个代码运行时间:集合操作的、多线程操作的、常用API操作的等等...
32 // 所以说每次都要修改这个类,是不行的。肿么办?
33 */
34
35 /*
36 * 功能抽象类
37 */
38 public abstract class GetTime {
39
40 public abstract void code();
41
42 public long getTime() {
43 long start = System.currentTimeMillis();
44 code();
45 long end = System.currentTimeMillis();
46 return end - start;
47 }
48
49 }
功能抽象类


1 package cn.itcast_01;
2
3 /*
4 * 具体实现类
5 * for循环程序
6 */
7 public class ForDemo extends GetTime {
8
9 @Override
10 public void code() {
11 for (int x = 0; x < 100000; x++) {
12 System.out.println(x);
13 }
14 }
15
16 }
具体实现类


1 package cn.itcast_01;
2
3 /*
4 * 测试类
5 */
6 public class GetTimeTest {
7 public static void main(String[] args) {
8 // GetTime gt = new GetTime();
9 // System.out.println(gt.getTime() + "毫秒");
10
11 GetTime gt = new ForDemo(); // 多态
12 System.out.println(gt.getTime() + "毫秒");
13
14 gt = new IODemo(); // 多态
15 System.out.println(gt.getTime() + "毫秒");
16 }
17 }
测试类


1 package cn.itcast_01;
2
3 import java.io.BufferedInputStream;
4 import java.io.BufferedOutputStream;
5 import java.io.FileInputStream;
6 import java.io.FileOutputStream;
7 import java.io.IOException;
8
9 /*
10 * 具体实现类
11 * IO程序
12 */
13 public class IODemo extends GetTime{
14
15 @Override
16 public void code() {
17 try {
18 BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src//cn//itcast_01//a.avi"));
19 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src//cn//itcast_01//b.avi"));
20 byte[] bys = new byte[1024];
21 int len = 0;
22 while ((len = bis.read(bys)) != -1) {
23 bos.write(bys, 0, len);
24 }
25 bos.close();
26 bis.close();
27 } catch (IOException e) {
28 e.printStackTrace();
29 }
30 }
31
32 }
具体实现类
B:装饰设计模式(IO流中用的多)
装饰设计模式的概述
装饰模式就是使用被装饰类的一个子类的实例,在客户端将这个子类的实例交给装饰类。是继承的替代方案。
优点
使用装饰模式,可以提供比继承更灵活的扩展对象的功能,它可以动态的添加对象的功能,并且可以随意的组合这些功能。
缺点
正因为可以随意组合,所以就可能出现一些不合理的逻辑。
示例代码如下:
// 复杂的装饰(多重装饰)
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter((new OutputStreamWriter(System.out)));
// 简单的装饰
Scanner sc = new Scanner(System.in);


1 package cn.itcast_02;
2
3 /*
4 * 手机接口
5 */
6 public interface Phone {
7 public abstract void call();
8 }
手机接口


1 package cn.itcast_02;
2
3 /*
4 * 手机实现类
5 */
6 public class IPhone implements Phone {
7
8 @Override
9 public void call() {
10 System.out.println("手机可以打电话了");
11 }
12
13 }
手机实现类


1 package cn.itcast_02;
2
3 /*
4 * 手机装饰抽象类
5 */
6 public abstract class PhoneDecorate implements Phone {
7
8 private Phone p;
9
10 public PhoneDecorate(Phone p) {
11 this.p = p;
12 }
13
14 @Override
15 public void call() {
16 this.p.call();
17 }
18 }
手机装饰抽象类


1 package cn.itcast_02;
2
3 /*
4 * 彩铃手机装饰具体类
5 */
6 public class RingPhoneDecorate extends PhoneDecorate {
7
8 public RingPhoneDecorate(Phone p) {
9 super(p);
10 }
11
12 @Override
13 public void call() {
14 System.out.println("手机可以听彩铃");
15 super.call();
16 }
17 }
听彩铃手机装饰具体类


1 package cn.itcast_02;
2
3 /*
4 * 听音乐手机装饰具体类
5 */
6 public class MusicPhoneDecorate extends PhoneDecorate {
7
8 public MusicPhoneDecorate(Phone p) {
9 super(p);
10 }
11
12 @Override
13 public void call() {
14 super.call();
15 System.out.println("手机可以听音乐");
16 }
17 }
听音乐手机装饰具体类


1 package cn.itcast_02;
2
3 import java.io.BufferedReader;
4 import java.io.BufferedWriter;
5 import java.io.InputStreamReader;
6 import java.io.OutputStreamWriter;
7 import java.util.Scanner;
8
9 /*
10 * 手机测试类
11 */
12 public class PhoneDemo {
13 public static void main(String[] args) {
14 Phone p = new IPhone(); // 多态
15 p.call();
16 System.out.println("------------");
17
18 // 需求:我想在接电话前,听彩铃
19 PhoneDecorate pd = new RingPhoneDecorate(p); // 多态
20 pd.call();
21 System.out.println("------------");
22
23 // 需求:我想在接电话后,听音乐
24 pd = new MusicPhoneDecorate(p);
25 pd.call();
26 System.out.println("------------");
27
28 // 需求:我要想手机在接电话前听彩铃,接电话后听音乐
29 // 自己提供听彩铃和听音乐的装饰类,在接电话前听彩铃,在接电话后听音乐,这样做的话,不好,太麻烦了。
30 // 下面这种方式可以解决问题,这就是装饰的优点:可以任意组合功能。
31 pd = new RingPhoneDecorate(new MusicPhoneDecorate(p));
32 pd.call();
33 System.out.println("----------");
34
35 // 想想我们在IO流中的使用
36 // InputStream is = System.in; // 字节流
37 // InputStreamReader isr = new InputStreamReader(is); // 把字节流转为字符流
38 // BufferedReader br = new BufferedReader(isr); // 把基本字符流转为高效字符流
39 // 复杂的装饰
40 BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
41 BufferedWriter bw = new BufferedWriter((new OutputStreamWriter(System.out)));
42
43 // 简单的装饰
44 Scanner sc = new Scanner(System.in);
45 }
46 }
手机测试类
C:适配器模式(GUI中用的多)
适配器设计模式的概述
将一个类的接口转换成另外一个客户希望的接口。从而使原来不能直接调用的接口变的可以调用。
优点
让本来不适合使用的接口变得适合使用。
缺点
一次只能适配一个类,使用有一定的局限性。
-----------------------------------------------------------------------------
3:JDK新特性
(1)JDK5(掌握)
自动装箱和拆箱(day13)
泛型(day16)
增强for循环(day16)
静态导入(day16)
可变参数(day16)
--------------------------------------
枚举(day27)
A:枚举的概述
是指将变量的值一一列出来,变量的值只限于列举出来的值的范围内。
举例:一周只有7天,一年只有12个月等。
回想单例设计模式:单例类是一个类只有一个实例。
那么多例类就是一个类有多个实例,但不是无限个数的实例,而是有限个数的实例。这才能是枚举类。
--------------------------------------
B:通过自己定义一个枚举类来演示案例
第一版
第二版
第三版
发现自己定义一个枚举类,比较麻烦,所以,java就提供了枚举类供我们使用。
格式是:只有枚举项的枚举类
public enum 枚举类名 {
枚举项1, 枚举项2, 枚举项3, ...;
}
--------------------------------------
C:注意事项
1.定义枚举类要用关键字enum。
2.所有枚举类都是Enum类的子类。
3.枚举类的第一行上必须是枚举项,最后一个枚举项后的分号是可以省略的,但是如果枚举类有其他的东西,这个分号就不能省略。建议永远不要省略。
4.枚举类中可以有构造方法,但必须是private的,它默认也是private的。枚举项的用法比较特殊:枚举("XXX")。
5.枚举类中也可以有抽象方法,但枚举项必须重写该方法。
6.枚举在switch语句中的使用。
D:枚举类中的常见方法
public final int compareTo(E o)
public final String name()
public final int ordinal()
public String toString()
public static <T> T valueOf(Class<T> enumType, String name)
public static values() 此方法虽然在JDK文档中查找不到,但每个枚举类都具有该方法,它遍历枚举类的所有枚举值非常方便。
--------------------------------------
(2)JDK6(很少见,了解)
(3)JDK7(理解)
二进制字面量(二进制的表现形式)
JDK7开始,终于可以用二进制来表示整数(byte,short,int和long)。
使用二进制字面量的好处是,可以使代码更容易被理解。语法非常简单,只要在二进制数值前面加0b或者0B即可。
举例:
int x = ob110110;
--------------------------------------
数字字面量可以出现下划线(用_分隔数据)
为了增强对数值的阅读性,如我们经常把数据用逗号分隔一样。
JDK7提供了_对数据分隔。
举例:
int x = 100_1000;
注意事项:
不能出现在进制标识和数值之间。
不能出现在数值开头和结尾。
不能出现在小数点旁边。
--------------------------------------
switch语句的表达式可是用字符串(day04)
泛型简化(泛型推断(也叫菱形泛型))
异常的多个catch合并(多catch的使用)(day19)
try-with-resources 语句(自动释放资源的用法)
try (必须是java.lang.AutoCloseable的子类对象) {...}
好处:
资源自动释放,不需要close()了。
把需要关闭资源的部分都定义在这里就ok了。
主要是流体系的对象是这个接口的子类(看JDK7的API)。
--------------------------------------
(4)JDK8(了解)
可以去网上了解资料。
1.接口中也可以有方法了(有默认方法、静态方法、私有方法(JDK9))。


1 /*
2 JDK8新特性之
3 接口中也可以有方法了
4 */
5 interface Inter {
6 // 抽象方法
7 public abstract void show();
8
9 // default方法(默认方法)
10 public default void defaultPrint() {
11 System.out.println("defaultPrint 我爱林青霞");
12 }
13
14 // static方法(静态方法)
15 public static void staticPrint() {
16 System.out.println("staticPrint 我爱林青霞");
17 }
18 }
19
20 // 实现类
21 class InterImpl implements Inter {
22 public void show() {
23 System.out.println("重写接口中的方法");
24 }
25 }
26
27 // 测试类
28 public class Demo01 {
29 public static void main(String[] args) {
30 // Inter.defaultPrint(); // 非静态方法不能直接调用
31 Inter.staticPrint();
32
33 Inter i = new InterImpl();
34 i.defaultPrint();
35 i.show();
36 }
37 }
JDK8新特性之接口中也可以有方法了(有默认方法和静态方法)
=============================================================================