[TOC]
反射机制 1.类加载概述 类加载概述:当程序要使用某个类时候,如果该类还未被加载到内存之中,则系统会通过加载、连接、初始化
来实现对这个类进行初始化
(1)加载:是指将class文件读入到内存,并为之创建一个Class对象,任何类被使用的时候系统都会创建一个Class对象
(2)连接:
验证:是否有正确的内部结构并和其他类协调一致;
准备:负责为类的静态成员分配内存,并设置默认初始值;
解析:将类的二进制数据中的符号引用替换成为直接引用
(3)初始化:就是我们以前讲过的初始化步骤
类加载时机流程:
创建类的实例;
访问类的静态变量或者为静态变量赋值;
使用类的静态方法
使用反射方式来强制创建某个类或者接口对应的java.lang.class对象;
初始化某个类的子类
直接使用java.exe命令来运行某个主类
2.类加载器概述 类加载器概述:负责将.class文件加载到内存中,并为之生成对应的class文件,虽然我们不需要关心类的加载机制,但是了解这个机制我们就能更好的理解程序的运行; 类加载器的分类:
BootStrp ClassLoader 根类加载器
Extension ClassLoader 扩展类加载器
System Classloader 系统类加载器
类的加载器的作用:
Bootstrap ClassLoader 根类加载器: 也被称为引导类加载器,负责Java核心类的加载;比如System/String类等在JDK的JRE里lib目录下rt.jar文件中;
Extension ClassLoader 扩展类加载器: 负责JRE的扩展目录中的jar包的加载,在JDK中JRE的lib目录下的ext目录;
System ClassLoader 系统类加载器: 负责在JVM启动时候加载来自Java命令的class文件,以及classpath环境变量所指定的jar包和类路径;
3.类的反射 描述:Java反射机制是运行状态中对任意一个类,都能知道这个类的所有属性和方法;对于任意一个对象都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为JAVA语言的反射机制,要想剖析一个类就必须先要获取到该类的字节码文件对象,而解剖使用的就是Class类中的方法,所以要先获取每个字节码文件对应的class类型的对象;
总结:反射Reflect一切都是赤裸裸的,包括私有的成员变量
;
Class类方法:
[TOC]
反射机制 1.类加载概述 类加载概述:当程序要使用某个类时候,如果该类还未被加载到内存之中,则系统会通过加载、连接、初始化
来实现对这个类进行初始化
(1)加载:是指将class文件读入到内存,并为之创建一个Class对象,任何类被使用的时候系统都会创建一个Class对象
(2)连接:
验证:是否有正确的内部结构并和其他类协调一致;
准备:负责为类的静态成员分配内存,并设置默认初始值;
解析:将类的二进制数据中的符号引用替换成为直接引用
(3)初始化:就是我们以前讲过的初始化步骤
类加载时机流程:
创建类的实例;
访问类的静态变量或者为静态变量赋值;
使用类的静态方法
使用反射方式来强制创建某个类或者接口对应的java.lang.class对象;
初始化某个类的子类
直接使用java.exe命令来运行某个主类
2.类加载器概述 类加载器概述:负责将.class文件加载到内存中,并为之生成对应的class文件,虽然我们不需要关心类的加载机制,但是了解这个机制我们就能更好的理解程序的运行; 类加载器的分类:
BootStrp ClassLoader 根类加载器
Extension ClassLoader 扩展类加载器
System Classloader 系统类加载器
类的加载器的作用:
Bootstrap ClassLoader 根类加载器: 也被称为引导类加载器,负责Java核心类的加载;比如System/String类等在JDK的JRE里lib目录下rt.jar文件中;
Extension ClassLoader 扩展类加载器: 负责JRE的扩展目录中的jar包的加载,在JDK中JRE的lib目录下的ext目录;
System ClassLoader 系统类加载器: 负责在JVM启动时候加载来自Java命令的class文件,以及classpath环境变量所指定的jar包和类路径;
3.类的反射 描述:Java反射机制是运行状态中对任意一个类,都能知道这个类的所有属性和方法;对于任意一个对象都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为JAVA语言的反射机制,要想剖析一个类就必须先要获取到该类的字节码文件对象,而解剖使用的就是Class类中的方法,所以要先获取每个字节码文件对应的class类型的对象;
总结:反射Reflect一切都是赤裸裸的,包括私有的成员变量
;
Class类方法:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public final class Class <T >extends Object implements Serializable , GenericDeclaration , Type , AnnotatedElement //常见方法: T newInstance () Constructor<T> getConstructor (类<?>... parameterTypes) Constructor<?>[] getConstructors () Field getField (String name) Field[] getFields () Field[] getDeclaredFields () Method getMethod (String name, 类<?>... parameterTypes) Method[] getMethods ()
补充类说明:1 2 3 4 5 public final class Constructor <T > extends Executable //常用方法 T newInstance (Object ... initargs ) //利用这 Constructor 对象创建和初始化的构造函数的声明类的一个新实例构造函数,用指定的初始化参数。
1 2 3 4 5 6 7 8 9 public final class Field extends AccessibleObject implements Member //方法 void set (Object obj , Object value ) //设置域为代表的这 Field 对象指定对象上的参数指定的新价值。 public void setAccessible (boolean flag ) throws SecurityException //该对象到指定的布尔值设置 accessible 旗。一个价值 true 表明反射的对象应当压制java 语言访问检查时可以将私有成员变量变成共有;
1 2 3 4 public final class Method extends Executable String getName () //返回的方法对象表示的方法的名称,作为一个 String 。 Object invoke (Object obj , Object ... args ) //调用底层的方法,这 方法对象表示,对指定对象的指定参数。 执行指定字节码中的方法
三种方式:
(1)Class类中静态方法forName读取配置文件
(2)静态属性class中锁对象
(3)Object 类的getClass()发布方法判断两个对象是否是同一个字解码文件
weiyigeek.top-
基础示例:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 package com.weiyigeek.reflect;import java.io.BufferedReader;import java.io.FileReader;public class Demo2_ReflectForName { public static void main (String[] args) throws Exception { Juicer j = new Juicer(); BufferedReader br = new BufferedReader(new FileReader("config.properties" )); String line; while ((line = br.readLine()) != null ) { Class clazz2 = Class.forName(line); Fruit o= (Fruit) clazz2.newInstance(); j.run(o); } } } interface Fruit { public void squeeze () ; } class Apple implements Fruit { @Override public void squeeze () { System.out.println("这是一杯 苹果 汁" ); } } class Orange implements Fruit { @Override public void squeeze () { System.out.println("这是一杯 橘子 汁" ); } } class Banana implements Fruit { @Override public void squeeze () { System.out.println("这是一杯 香蕉 汁" ); } } class Juicer { public void run (Fruit a) { a.squeeze(); } }
config.properties1 2 com.weiyigeek.reflect.Orange com.weiyigeek.reflect.Apple
基础示例:
1.创建对象的三种方式
2.利用字节码中无参构造和有参构造创建对象
2.1 class 类的newInstance()方法是使用该类无参的构造函数创建对象
2.2 调用Class类中的getConstructor(String.class,int.class)方法获取一个指定的构造函数然后再调用Constructor类的newInstance先有参函数创建对象
3.通过反射获取成员变量并使用分为共有public和私有private
4.通过反射获取成员方法并使用分为 无参方法 和 有参方法
5.通过反射越过泛型的监测,泛型特性:只在编译期间有效,在运行期会被擦除掉;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 package com.weiyigeek.reflect;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.ArrayList;import com.weiyi.Collection.Students;public class Demo1_Reflect { public static void main (String[] args) throws Exception { Class clazz1 = Class.forName("com.weiyi.Collection.Students" ); Class clazz2 = Students.class; Students clazz = new Students(); Class clazz3 = clazz.getClass(); System.out.println(clazz1 == clazz2); System.out.println(clazz3 == clazz2); Constructor<Students> cs = clazz1.getConstructor(String.class,int .class); Students s = (Students) cs.newInstance("有参构造" ,1024 ); System.out.println(s); Field f = clazz1.getDeclaredField("name" ); f.setAccessible(true ); f.set(s, "Field" ); System.out.println(s); Method m = clazz1.getMethod("reflectDemo" ); m.invoke(s); Method m1 = clazz1.getMethod("reflectDemo" , int .class); m1.invoke(s, 1024 ); ArrayList<Integer> list = new ArrayList<>(); list.add(1 ); list.add(2 ); Class clazz6 = Class.forName("java.util.ArrayList" ); Method m2 = clazz6.getMethod("add" ,Object.class); m2.invoke(list, "abc" ); System.out.println("反射绕过泛型: " + list); } }
执行结果:1 2 3 4 5 6 7 true true Students [name=有参构造, age=1024 ] Students [name=Field, age=1024 ] 反射获取的无参方法! 反射获取的有参方法,a = 1024 反射绕过泛型: [1 , 2 , abc]
综合实例:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package com.weiyigeek.reflect;import java.lang.reflect.Field;public class Demo3_ReflectTool { public Demo3_ReflectTool () {}; public void setProperty (Object obj, String propertyName, Object value) throws Exception { Class clazz = obj.getClass(); Field f = clazz.getDeclaredField(propertyName); f.setAccessible(true ); f.set(obj, value); } public void run () { System.out.println("Welcome to China" ); } } package com.weiyigeek.reflect;import java.io.BufferedReader;import java.io.FileReader;import com.weiyi.Collection.Students;public class Demo4_UseReflect { public static void main (String[] args) throws Exception { Students stu = new Students("张三" ,1024 ); System.out.println("修改前:" + stu); Demo3_ReflectTool tool = new Demo3_ReflectTool(); tool.setProperty(stu,"name" ,"王五" ); System.out.println("修改后:" + stu); BufferedReader br = new BufferedReader(new FileReader("config.properties" )); Class clazz = Class.forName(br.readLine()); Demo3_ReflectTool dr = (Demo3_ReflectTool) clazz.newInstance(); dr.run(); } }
执行结果:1 2 3 修改前:Students [name=张三, age=1024 ] 修改后:Students [name=王五, age=1024 ] Welcome to China
补充知识:
框架相当是一个房子,反射就相当于对房子不断的修改装修;
4.动态代理 动态代理概述:
代理:本类应该自己做得事情,请了别人来做,被请的人就是代理对象
;
举例:春节回家买票让人代买,强最新版本的Iphone手机
动态代理:在程序运行过程中产生的这个对象,而程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以动态代理其实就是通过反射来生成的一个代理
;
在Java中java.lang.reflect
包下提供了一个Proxy类和一个InvocationHandler接口,通过实用这个类和接口就可以生成的动态代理对象. JDK提供的代理只能针对于接口做代理,我们有更强大的代理cglib,Proxy类中的方法创建动态代理类对象;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 java.lang.Object java.lang.reflect.Proxy public class Proxy extends Object implements Serializable //类方法 public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h); //InvocationHandler 接口 返回指定的接口,将方法调用指定的调用处理程序的代理类的一个实例。 InvocationHandler Object invoke (Object proxy, Method method, Object[] args)
实际案例:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 package com.weiyigeek.reflect;interface Demo5_User { public void login () ; public void run () ; } package com.weiyigeek.reflect;public class Demo5_UserImp implements Demo5_User { @Override public void login () { System.out.println("- 1.登录操作" ); } @Override public void run () { System.out.println("- 2.运行操作" ); } } package com.weiyigeek.reflect;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class Demo5_MyInvocationHandler implements InvocationHandler { private Object target; public Demo5_MyInvocationHandler (Object target) { this .target = target; } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { System.out.println("权限校验" ); method.invoke(target, args); System.out.println("日志记录" ); return null ; } } package com.weiyigeek.reflect;import java.lang.reflect.Proxy;public class Demo5_Test { public static void main (String[] args) { Demo5_UserImp ui = new Demo5_UserImp(); Demo5_MyInvocationHandler dm = new Demo5_MyInvocationHandler(ui); Demo5_User du = (Demo5_User)Proxy.newProxyInstance(ui.getClass().getClassLoader(), ui.getClass().getInterfaces(), dm); du.login(); du.run(); } }
执行结果:1 2 3 4 5 6 7 权限校验 - 1.登录操作 日志记录 权限校验 - 2.运行操作 日志记录
模板设计模式 模板template设计模式概述:模板方法模式就是一个定义一个算法得骨架,将具体得算法延迟到子类中来实现;
优点:使用模板方式模式,再定义算法骨架得同时,可以很灵活得实现具体得算法,满足用户灵活多变得需求:
缺点: 如果算法骨架有修改得画,则需要修改抽象类;
类设计模式总结:1 2 3 4 5 6 1.装饰设计:装饰接口实现的类再次进行封装; 2.单例:单例类是一个类只有一个实例; 3.简单工厂模式: 4.工厂方法 5.适配器 6.模板
模板设计案例:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.weiyigeek.reflect;public class Demo6_Template { public static void main (String[] args) { Temp tmp = new Temp(); System.out.println("花费时间为: " + tmp.getRunTime()); } } abstract class GetRunTime { public final long getRunTime () { long start = System.currentTimeMillis(); code(); long end = System.currentTimeMillis(); return end - start; } public abstract void code () ; } class Temp extends GetRunTime { @Override public void code () { for (int i = 0 ; i < 10000 ; i++) System.out.println(i); } }
运行结果:
枚举类 描述:枚举是指将变量得值一一列举出来,变量得值只限于列举处理得值得范围内; 例如: 与C语言中枚举是一样,一个变量只能是再枚举范围内得值,比如一周只有7天,一年只有12个月等等;
与单例设计模式,那么多例类就是一个类有多个实例,但是部署无限个数得实例。而是有限个数得实例,这才能是枚举类。
关键技术:1 2 3 4 5 6 7 1.自动拆装箱 2.泛型 3.可变参数 4.静态导入 5.增强for 循环 6.互斥锁 7.枚举
枚举类常见方法:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 java.lang.Object java.lang.Enum<E> public abstract class Enum <E extends Enum <E >>extends Object implements Comparable <E >, Serializable //常规方法 int ordinal () //返回此枚举常量的顺序(位置在枚举声明,在初始常数是零分序号)。 int compareTo (E o ) //这个枚举与指定对象按顺序比较。 int hashCode () //返回此枚举常量的哈希代码。 String name () //返回此枚举常量的名称,如宣布在其枚举声明。枚举项String toString () //返回此枚举常量的名称,包含在声明。 static <T extends Enum <T >>T valueOf (class <T > enumType , String name ) //通过字节码码对象获取枚举项 ;Values()
注意事项:
定义枚举类要用关键字enum
所有的枚举类都是Enum的子类
枚举类的第一行上必须是枚举项,最后一个枚举项后的分号是可以省略的,但是如果枚举类有其他东西就不能省略 “;”
枚举类有构造器但必须是private其实默认修饰符也是它
枚举类也可以有抽象的方法(默认都是抽象类),但是枚举项必须重写该方法
枚举在Switch语句中的使用
实例1:自己实现枚举 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package com.weiyigeek.reflect;public class Demo7_Week { public static final Demo7_Week Mon = new Demo7_Week(); public static final Demo7_Week Tue = new Demo7_Week(); public static final Demo7_Week Wen = new Demo7_Week(); private Demo7_Week () {}; public static final Demo7_Week Thu = new Demo7_Week("星期四" ); public static final Demo7_Week Fri= new Demo7_Week("星期五" ); public static final Demo7_Week Sat= new Demo7_Week("星期六" ); private String week; private Demo7_Week (String w) { this .week = w; } public String getWeek () { return week; } } package com.weiyigeek.reflect;public abstract class Demo7_Week1 { public static final Demo7_Week1 Sun = new Demo7_Week1("星期天" ) { public void show () { System.out.println("星期天" ); } }; private String week; public Demo7_Week1 (String w) { this .week = w; } public abstract void show () ; }
Demo7_Enumerate.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.weiyigeek.reflect;public class Demo7_Enumerate { public static void main (String[] args) { Demo7_Week mon = Demo7_Week.Mon; Demo7_Week tue = Demo7_Week.Tue; Demo7_Week wed = Demo7_Week.Wen; System.out.println("方式1:" ); System.out.println(mon); Demo7_Week Thu = Demo7_Week.Thu; Demo7_Week Fri = Demo7_Week.Fri; Demo7_Week Sat = Demo7_Week.Sat; System.out.println("方式2:" ); System.out.println(Thu.getWeek()); Demo7_Week1 sun = Demo7_Week1.Sun; System.out.println("方式3:" ); sun.show(); } }
执行结果:1 2 3 4 5 6 7 8 方式1 : com.weiyigeek.reflect.Demo7_Week@15 db9742 方式2 : 星期四 方式3 : 星期天
实例2:利用enum实现枚举类
实际案例:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 package com.weiyigeek.reflect;public enum Week1 { Mon,Tue,Wed("星期三" ); private Week1 () {}; private String w; private Week1 (String w) { this .w = w; } public String getWeek () { return w; } } package com.weiyigeek.reflect;public enum Week2 { MON("星期一" ){ public void show () { System.out.println("星期一" ); } },TUE("星期二" ){ public void show () { System.out.println("星期二" ); } }; private String w; private Week2 (String w) { this .w = w; }; private String getWeek () { return w; } public abstract void show () ; } package com.weiyigeek.reflect;public class EnumDemo { public static void main (String[] args) { Week1 w = Week1.Mon; System.out.println("方法1:" ); System.out.println(w); Week1 w1 = Week1.Wed; System.out.println("方法2:" ); System.out.println(w1.getWeek()); Week2 w2 = Week2.MON; System.out.println("方法3:" ); w2.show(); System.out.println("方法4:" ); Week2 w3 = Week2.MON; switch (w3) { case MON: System.out.println("星期 1 " ); break ; case TUE: System.out.println("星期 2 " ); } } }
运行结果:1 2 3 4 5 6 7 8 方法1: Mon 方法2: 星期三 方法3: 星期一 方法4: 星期 1
实例3:enum常见方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.weiyigeek.reflect;public class EnumWeek { public static void main (String[] args) { Week2 mon = Week2.MON; Week2 tue = Week2.TUE; Week2 wed = Week2.WED; System.out.println("枚举项都是有编号的:" + mon.ordinal() + ", " + tue.ordinal()); System.out.println("比较枚举项的序号顺序:" + mon.compareTo(tue)); System.out.println("枚举项HasCode:" + wed.hashCode()); System.out.println("枚举项名称: " + wed.name()); System.out.println("枚举项内容: " + wed.toString()); System.out.print("通过字节码码对象获取枚举项 : " ); Week2 mon1 = Week2.valueOf(Week2.class, "MON" ); System.out.println(mon1); System.out.println("Value() JDK文档中没有但是非常好用:" ); Week2[] w2 = Week2.values(); for (Week2 wk : w2) { System.out.println(wk); } } }
执行结果:1 2 3 4 5 6 7 8 9 枚举项都是有编号的:0 , 1 比较枚举项的序号顺序:-1 枚举项HasCode:366712642 枚举项名称: WED 枚举项内容: 星期三 通过字节码码对象获取枚举项 : 星期一 星期一 星期二 星期三
JDK1.7新特性
二进制字面量 (0b110) = 6
数字字面量可以出现下划线
Switch 语句可以用字符串
泛型简化菱型泛型
异常多个catch合并每个异常用 或(|) 拼接
try-witch-resource 语句该版本标准的异常处理代码;
实际案例:1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.weiyigeek.reflect;public class Demo8_JDK7Speciality { public static void main (String[] args) { System.out.print("二进制字面量:" ); System.out.println(0b1111 + " " ); System.out.println(0b11_11 ); System.out.print("数值字面量:" ); System.out.println(1000_1111 ); } } 二进制字面量:15 15 数值字面量:10001111
JDK1.8新特性
接口中可以定义有方法体的方法,如果是非静态必须采用default修饰,如果是静态的就可不用了;
局部内部类方他所在的方法中的局部变量必须用final修饰为什么?
因为当调用这个方法时候局部变量如果没有final修饰,他的生命周期和方法的生命周期是一致的,当方法弹栈这个局部变量也会消失,那么如果局部内部类对象还没马上消失想用这个局部变量就没有了。
如果采用final修饰会在类加载的时候进行常量池,即使方法弹栈常量池还是存在也可以继续使用;
基础实例: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package com.weiyigeek.reflect;public class Demo9_JDK8Speciality { public static void main (String[] args) { Demo d = new Demo(); d.print(); Inter.method(); d.run(); } } interface Inter { public default void print () { System.out.println("Hello World!" ); } public static void method () { System.out.println("Static Method" ); } } class Demo implements Inter { public void run () { int flag = 1024 ; class Inner { public void num () { System.out.println("flag = " + flag); } } Inner i = new Inner(); i.num(); } }
执行结果:1 2 3 Hello World! Static Method flag = 1024