java基础学习_反射、装饰模式、JDK新特性_day27总结
============================================================================= ============================================================================= 涉及到的知识点有: 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 }
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 }
-------------------------------------- (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 }
1 package cn.itcast.test; 2 3 public class Teacher { 4 public void love() { 5 System.out.println("爱生活,爱青霞"); 6 } 7 }
1 package cn.itcast.test; 2 3 public class Worker { 4 public void love() { 5 System.out.println("爱生活,爱老婆"); 6 } 7 }
1 className=cn.itcast.test.Worker 2 methodName=love
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 }
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 }
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 }
1 package cn.itcast_06; 2 3 public interface StudentDao { 4 public abstract void login(); 5 6 public abstract void regist(); 7 }
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 }
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 }
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 }
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 }
=============================================================================
我的GitHub地址:https://github.com/hei*gjun
我的博客园地址:http://www.cnblogs.com/chenmingjun
我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun
Copyright ©2018 黑泽明军
【转载文章务必保留出处和署名,谢谢!】