本文共 36282 字,大约阅读时间需要 120 分钟。
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 @Override43 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 // 方式A29 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); // false36 System.out.println(c == c2); // true37 38 // 方式B39 Class c3 = Person.class;40 // int.class;41 // String.class;42 System.out.println(c == c3); // true43 44 // 方式C45 // ClassNotFoundException 需要类的全路径(带包名的路径)46 Class c4 = Class.forName("cn.itcast_01.Person");47 System.out.println(c == c4); // true48 }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 ConstructorgetConstructor(Class ... parameterTypes) 获取单个的公共构造方法39 // Class类的成员方法:public Constructor 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 ConstructorgetConstructor(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.Worker2 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 * className17 * methodName18 * 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的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢?)
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的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢?)10 */11 public class ArrayListDemo {12 public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException,13 IllegalArgumentException, InvocationTargetException {14 15 // 创建集合对象16 ArrayList array = new ArrayList ();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方法,传入的值是hello31 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 @Override34 public String toString() {35 return name + "---" + age;36 }37 }38 39 class Dog {40 char sex;41 float price;42 43 @Override44 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 @Override11 public void delete() {12 System.out.println("删除功能");13 }14 15 @Override16 public void update() {17 System.out.println("修改功能");18 }19 20 @Override21 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 @Override11 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 @Override14 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 @Override10 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 @Override16 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 @Override15 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 @Override13 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 @Override13 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 staticT valueOf(Class 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地址: 我的博客园地址: 我的蚂蚁笔记博客地址: Copyright ©2018 黑泽明军 【转载文章务必保留出处和署名,谢谢!】