java是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由Sun Microsystems公司于1995年5月推出的Java程序设计语言和Java平台(即JavaEE, JavaME, JavaSE)的总称。本站提供基于Java框架struts,spring,hibernate等的桌面应用、web交互及移动终端的开发技巧与资料
保持永久学习的心态,将成就一个优秀的你,来 继续搞起java知识。
19 反射
19.1 类的加载概述和加载时机
19.1.1 类的加载概述
当程序要使用某个类时,如果该类还未加载到内存中,则系统会通过加载、连接、初始化三步来实现对这个类进行初始化。加载:
指将class文件读入内存中,并为之创建一个Class对象,任何类被使用时系统都会建立一个Class对象。
连接:
验证:是否有正确的内部结构,并和其他类协调一致
准备:负责为类的静态成员分配内存,并设置默认初始化值
解析:将类的二进制数据中的符号引用替换为直接引用
初始化:即初始化步骤。
19.1.2 加载时机
创建类的实例访问类的静态变量,或者为静态变量赋值
调用类的静态方法
使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
初始化某个类的子类
直接使用java.exe命令来运行某个主类。
19.2 类加载器的概述和分类
19.2.1 类加载器的概述
负责将.class文件加载到内存中,并为之生成对应的Class对象。虽然我们并不关心类加载机制,但是了解这个机制可以更好理解程序运行。
19.2.2 类加载器的分类
Bootstrap ClassLoader 根类加载器
Extension ClassLoader 扩展类加载器
System ClassLoader 系统类加载器
19.2.3 类加载器的作用
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包和类路径
19.3 反射概述
概述 JAVA反射机制是运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用他的任意一个方法和属性;
这种动态获取的信息以及动态调用的方法的功能称为java语言的反射机制;
想要解剖一个类,必须先要获取到该类的字节码文件对象;
而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应Class类型的对象。
例1:获取字节码文件对象的三种方式
Person.java
1 package com.mirror.gf; public class Person { private String name; private int age; public Person(){ super(); } public Person(String name,int age){ super(); this.name = name; this.age = age; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }
Demo_Reflect.java
1package com.mirror.gf; public class Demo_Reflect { public static void main(String[] args) throws ClassNotFoundException{ Class clazz1 = Class.forName("com.mirror.gf.Person"); Class clazz2 = Person.class; Person p = new Person(); Class clazz3 = p.getClass(); System.out.println(clazz1== clazz2); System.out.println(clazz2== clazz3); } }
运行结果:
ture
ture
例2:Class.forName()读取配置文件举例Demo2_Reflect.java
1package com.mirror.gf; import java.io.BufferedReader; import java.io.FileReader; public class Demo2_Reflect { /** * @param args * @throws Exception */ public static void main(String[] args) throws Exception { //使用反射与配置文件 BufferedReader br = new BufferedReader(new FileReader("config.properties")); Class clazz = Class.forName(br.readLine()); Fruit f = (Fruit) clazz.newInstance(); Eat eat = new Eat(); eat.run(f); br.close(); //未用反射 /*Eat eat = new Eat(); eat.run(new Orange());*/ } } interface Fruit{ public void eat(); } class Apple implements Fruit{ public void eat(){ System.out.println("吃苹果"); } } class Orange implements Fruit{ public void eat(){ System.out.println("吃橙子"); } } class Eat{ public void run(Fruit f){ f.eat(); } }
配置文件congfig.properties:
com.mirror.gf.Apple
运行结果:吃苹果
例3:通过反射获取带参构造方法并使用 Class类的newInstance()方法时使用该类无参的构造函数,如果一个类没有无参的构造函数,就不能这样创建,可以调用Class类的getConstructor(String.class,int.class)方法获取一个指定的构造函数然后再调用Constructor类的newInstance(“张三”,20)方法创建对象
Ps:建立的项目需要jdk1.5以上才能正常使用getConstructor的方法
Person.java
1package com.mirror.gf; public class Person { private String name; private int age; public Person(){ super(); } public Person(String name,int age){ super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }
Demo3_Constructor.java
1 package com.mirror.gf; import java.lang.reflect.Constructor; public class Demo3_Constructor { public static void main(String[] args) { try { Class clazz = Class.forName("com.mirror.gf.Person"); //通过无参构造创建对象 //Person p = (Person) clazz.newInstance(); //获取有参构造 Constructor c = clazz.getConstructor(String.class,int.class); //通过有参构造对象 Person p = (Person) c.newInstance("Gabriel",27); System.out.print(p); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } //通过无参构建对象 /* Person p = (Person) clazz.newInstance(); System.out.print(p);*/ // Constructor c = clazz.getConstructor(String.class,int.class); } }
运行结果:Person [name=Gabriel, age=27]
例4:通过反射获取成员变量并使用Class.getField(String)方法获取类中的指定字段(可见的),如果是私有的可以用getDeclareField(“name”)方法获取,通过set(obj,”李四”)方法可以设置指定对象上该字段的值,如果是私有的需先调用setAccessible(true)设置访问权限,用获取的字段调用get(obj)可以获取指定对象中该字段的值
Demo3_Field.java
1package com.mirror.gf; import java.lang.reflect.Constructor; import java.lang.reflect.Field; public class Demo4_Field { /** * Class.getField(String)方法获取类中的指定字段(可见的),如果是私有的可以用getDeclareField("name") * 方法获取,通过set(obj,"李四")方法可以设置指定对象上该字段的值,如果是私有的需先调用setAccessible(true)设置访问权限, * 用获取的字段调用get(obj)可以获取指定对象中该字段的值 * @throws Exception */ public static void main(String[] args) throws Exception { Class clazz = Class.forName("com.mirror.gf.Person"); Constructor c = clazz.getConstructor(String.class,int.class); //通过有参构造对象 Person p = (Person) c.newInstance("Gabriel",27); //获取名字字段 Field f = clazz.getDeclaredField("name"); //去除私有权限 f.setAccessible(true); //修改姓名 f.set(p, "李四"); System.out.print(p); } }
例5:通过反射获取方法并使用
Class.getMethod(String,Class…)和Class.getDeclaredMethod(String,Class…)方法可以获取类中的指定方法,调用invoke(Object,Object…)可以调用该方法,Class.getMethod(“eat”)invoke(obj),Class.getMethod(“eat”,int.class)invoke(obj,10)
Person.java
1 package com.mirror.gf; public class Person { private String name; private int age; public Person(){ super(); } public Person(String name,int age){ super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } public void eat(){ System.out.println("吃了午饭"); } public void eat(int num){ System.out.println("今天吃了"+num+"顿"); } }
Demo5_Method.java
1package com.mirror.gf; import java.lang.reflect.Constructor; import java.lang.reflect.Method; public class Demo5_Method { /** *Class.getMethod(String,Class...)和Class.getDeclaredMethod(String,Class...)方法可以获取类中的指定方法, *调用invoke(Object,Object...)可以调用该方法, *Class.getMethod("eat")invoke(obj),Class.getMethod("eat",int.class)invoke(obj,10) * @throws Exception */ public static void main(String[] args) throws Exception { Class clazz = Class.forName("com.mirror.gf.Person"); Constructor c = clazz.getConstructor(String.class,int.class); Person p = (Person) c.newInstance("Gabriel",27); Method m = clazz.getMethod("eat"); m.invoke(p, null); Method m1 = clazz.getMethod("eat", int.class); m1.invoke(p, 3); } }
运行结果:
吃了午饭
今天吃了3顿
例6:通过反射越过泛型检查 泛型只在编译期有效,在运行期会被擦除掉;故可通过反射拿到编译后的字节码文件进行操作,从而越过泛型进行操作
1package com.mirror.gf; import java.lang.reflect.Method; import java.util.ArrayList; public class Test1 { /** *泛型只在编译期有效,在运行期会被擦除掉 * @throws Exception */ public static void main(String[] args) throws Exception { ArrayList<Integer> list = new ArrayList(); list.add(1); list.add(2); Class clazz = Class.forName("java.util.ArrayList"); Method m = clazz.getMethod("add",Object.class); m.invoke(list, "abc"); System.out.println(list); } }
例7:通过反射写一个通用的设置某个对象的某个属性为指定的值
Test2.java
1package com.mirror.gf; public class Test2 { /** * @param args * @throws Exception */ public static void main(String[] args) throws Exception { Student s = new Student("张三",20); System.out.println(s); Tools t = new Tools(); t.setProperty(s, "name", "李四"); System.out.println(s); } } class Student{ private String name; private int age; public Student(String name, int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String toString() { return "Student [name=" + name + ", age=" + age + "]"; } }
Tools.java
1package com.mirror.gf; import java.lang.reflect.Field; //此方法可以将obj对象中名为propertyName的属性值设置为value public class Tools { 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); } }
运行结果:
Student [name=张三, age=20]
Student [name=李四, age=20]
例8: 已知一个类如下:
1 package com.mirror.gf; public class DemoClass { public void run(){ System.out.println("welcome to GuangZhou"); } }
1、写一个properties格式的配置文件,配置类的完整名称;
2、写一个程序,读取这个properties配置文件,获得类的完整名称并加载这个类,用反射的方式运行run方法
配置文件name.properties内容为:com.mirror.gf.DemoClass
Test3.java
1package com.mirror.gf; import java.io.BufferedReader; import java.io.FileReader; import java.lang.reflect.Method; public class Test3 { /** * @param args * @throws Exception */ public static void main(String[] args) throws Exception { BufferedReader br = new BufferedReader(new FileReader ("name.properties")); Class clazz = Class.forName(br.readLine()); DemoClass dc = (DemoClass) clazz.newInstance(); Method m = clazz.getMethod("run"); m.invoke(dc); //或者直接运行 dc.run(); } }
运行结果:
welcome to GuangZhou
welcome to GuangZhou
19.4 动态代理
动态代理就是通过反射来生成一个代理;利用代理可以在运行时创建一个实现了一组给定接口的新类;
在Java.lang.reflect包中提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就就可以生成动态代理对象。JDK只能针对接口做代理。
例:
MyInvocationHandler.java
1package com.gf.AutoProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class MyInvocationHandler implements InvocationHandler { private Object target; public 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); //执行被代理target对象的方法 System.out.println("日志记录"); return null; } }
Student.java
1package com.gf.AutoProxy; public interface Student { public void login(); public void submit(); }
StudentImp.java
1 package com.gf.AutoProxy; public class StudentImp implements Student { @Override public void login() { System.out.println("登录"); } @Override public void submit() { System.out.println("提交"); } }
Test1.java
1package com.gf.AutoProxy; import java.lang.reflect.Proxy; public class Test1 { public static void main(String[] args) { StudentImp si = new StudentImp(); si.login(); si.submit(); System.out.println("--------------------"); MyInvocationHandler mi = new MyInvocationHandler(si); Student s = (Student) Proxy.newProxyInstance(si.getClass().getClassLoader(), si.getClass().getInterfaces(), mi); s.login(); s.submit(); } }
运行结果:
登录
提交
——————–
权限校验
登录
日志记录
权限校验
提交
日志记录
19.5 设计模式
模板方法模式就是定义一个算法的股价,而将具体的算法延迟到子类中实现; 例:
1package com.gf.TemplateMethod; public class Demo1_Template { /** * @param args */ public static void main(String[] args) { /*long start = System.currentTimeMillis(); for(int i =0;i<10000;i++){ System.out.println("1"); } long end = System.currentTimeMillis(); System.out.println(end-start);*/ Demo d = new Demo(); System.out.print(d.getTime()); } } abstract class GetTime{ //防止被子类重写 public final long getTime(){ long start = System.currentTimeMillis(); code(); long end = System.currentTimeMillis(); return end-start; } abstract void code(); } class Demo extends GetTime{ @Override void code() { int i = 0; while(i<10000){ System.out.print(true); i++; } } }
运行结果:28
20 枚举类的实现
枚举类要用关键字enum所有枚举类都是Enum的子类
枚举类第一行必须是枚举项,最后一个枚举项后的分号可省略,但是如果枚举类有其他项,这个枚举类不能省略;
枚举类可以由构造器,但必须是private,默认也为private;
枚举类也可有抽象方法,但是枚举项必须重写该方法;
枚举类在switch中的应用,如例3
20.1 自定义实现枚举类
枚举概述:将变量的值一一列举出来,变量的值只限于列举出来的值的范围内,例如一周只有7天,一年只有12个月;例1:
Demo1_Enum.java
1package com.gf.enumeration; public class Demo1_Enum { /** * @param args */ public static void main(String[] args) { //第一种方式:空参构造 demo(); //第二种方式:有参构造 Week2 mon = Week2.MON; System.out.println(mon.getName()); //第三种方式 Week3 mon1 = Week3.MON; mon1.show(); } private static void demo() { Week mon = Week.MON; Week tue = Week.TUE; Week wed = Week.WED; System.out.println(mon.getMon()); } }
Week.java
1package com.gf.enumeration; public class Week { public static final Week MON = new Week(); public static final Week TUE = new Week(); public static final Week WED = new Week(); private Week(){ } public static String getMon() { return "周一"; } public static String getTue() { return "周二"; } public static String getWed() { return "周三"; } }
Week2.java
1package com.gf.enumeration; public class Week2 { public static final Week2 MON = new Week2("周一"); public static final Week2 TUE = new Week2("周二"); public static final Week2 WED = new Week2("周三"); private String name; private Week2(String name){ this.name= name; } public String getName() { return name; } }
Week3.java
1package com.gf.enumeration; public abstract class Week3 { public static final Week3 MON = new Week3("周一"){ public void show(){ System.out.println("周一"); } }; public static final Week3 TUE = new Week3("周二"){ public void show(){ System.out.println("周二"); } }; public static final Week3 WED = new Week3("周三"){ public void show(){ System.out.println("周三"); } }; private String name; private Week3(String name){ this.name= name; } public String getName() { return name; } public abstract void show(); }
运行结果:
周一
周一
周一
20.2 通过enum实现枚举类
enum类中自带toString方法例2:
Demo1_Enum.java
1package com.gf.TemplateMethod2; public class Demo1_Enum { /** * @param args */ public static void main(String[] args) { //无参构造 demo(); //有参构造 Week2 mon = Week2.MON; System.out.println(mon.getName()); //带抽象方法 Week3 mon1 = Week3.MON; mon1.show(); } private static void demo() { Week mon = Week.MON; System.out.println(mon); } }
Week.java
1package com.gf.TemplateMethod2; public enum Week { MON,TUE,WED; }
Week2.java
1package com.gf.TemplateMethod2; public enum Week2 { MON("星期一"),TUE("星期二"),WED("星期三"); private String name; private Week2(String name){ this.name = name; } public String getName() { return name; } }
Week3.java
1package com.gf.TemplateMethod2; public enum Week3 { MON("星期一"){ public void show(){ System.out.println("星期一"); } }, TUE("星期二"){ public void show(){ System.out.println("星期二"); } }, WED("星期三"){ public void show(){ System.out.println("星期三"); } }; private String name; private Week3(String name){ this.name = name; } public String getName() { return name; } public abstract void show(); }
运行结果:
MON
星期一
星期一
例3:枚举类在switch中的应用
1package com.gf.TemplateMethod2; public class Demo1_Enum { /** * @param args */ public static void main(String[] args) { //无参构造 demo(); //有参构造 demo2(); //带抽象方法 demo3(); Week3 mon = Week3.TUE; switch(mon){ case MON: System.out.println("星期一"); break; case TUE: System.out.println("星期二"); break; case WED: System.out.println("星期三"); break; } } private static void demo2() { Week2 mon = Week2.MON; System.out.println(mon.getName()); } private static void demo3() { Week3 mon1 = Week3.MON; mon1.show(); } private static void demo() { Week mon = Week.MON; System.out.println(mon); } }
Java基础反射枚举
因为水平有限,难免有疏忽或者不准确的地方,希望大家能够直接指出来,我会及时改正。一切为了知识的分享。
后续会有更多的精彩的内容分享给大家。