一:什么是反射
在认识反射之前,首先回顾下什么是正向处理。如果我们需要实例化一个对象,首先需要导入这个类的包,在这个包中找这个类:
package CODE.反射;
import java.util.Date;
public class Fan {
public static void main(String[] args) {
Date date =new Date();
System.out.println(date);
}
}
实例化一个Date对象,需要根据包名.类名来找到Date类。
而反射是相反:是根据对象来取得对象的来源信息。
也就是对象的反向处理。根据对象倒推类的组成。
反射核心处理在于Object类的方法:
//取得类的Class 对象(Class是一个类)
public final native Class<?> getClass();
package CODE.反射;
import java.util.Date;
public class Fan {
public static void main(String[] args) {
Date date =new Date();
//取得data对象的Date类信息
System.out.println(date.getClass()); //class java.util.Date
}
}
Class对象的3种实例化方法:
任何一个类的Class对象由JVM加载类后产生(该对象在JVM全局唯一)。用户调用指定方法来取得该类对象。
- 任何类的对象可以通过Object类提供的getClass()取得该类Class对象
- “类名称.class”可以直接根据某个具体的类来取得Class对象
- 调用Class类的静态方法Class.forName(String className) throws
ClassNotFoundException,传入类的全名称来取得其Class对象。
import java.util.Date;
public class Fan {
public static void main(String[] args) throws ClassNotFoundException {
Date date =new Date();
Class<?> cls1 =date.getClass();
Class<?> cls2=Class.forName("java.util.Date");
Class<?> cls3=Date.class;
System.out.println(cls1); //class java.util.Date
System.out.println(cls1); //class java.util.Date
System.out.println(cls1); //class java.util.Date
}
}
class java.util.Date 前面的class表明是一个类对象。
从理论和实例看出,只有第一种方式需要实例化对象来取得Class对象,其余2种方式不会产生实例化对象。那么取得了Class类对象后可以通过反射来实例化对象。
用反射来实例化对象
在Class类中有一个方法可以通过Class对象来实例化对象:
public T newInstance() throws InstantiationException, IllegalAccessException
只能调用类中无参构造,且无参构造只能是public权限。
只有当实例化出Class对象后才可以用Class对象调newInstance。
例:
通过反射实例化对象
import java.util.Date;
public class Fan {
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
Class<?> cls1=Date.class; //Class对象
Object obj=cls1.newInstance(); //实例化对象,相当于new java.util.Date()
System.out.println(obj);
}
}
完善简单工厂模式----利用反射
之前在写工厂模式时,在Factory里new对象,但是有一个缺陷,就是新增一个类,需要在Factory里再添加一个分支来new对象。可以参考这篇博客:
但是有了反射后,可以用Class 对象来实例化对象(有2种产生Class对象不需要实例化对象:Class.forName()和类名.class)
代码如下:
package CODE.反射;
//工厂模式---反射
interface Study
{
void need();
}
class Pen implements Study
{
public void need()
{
System.out.println("需要一直笔");
}
}
class Book implements Study
{
public void need()
{
System.out.println("需要一个本子");
}
}
class Pencil implements Study
{
public void need()
{
System.out.println("需要一只铅笔");
}
}
class Factory1
{
public static Study getStudy(String className)
{
Study study=null;
try {
//cls是一个Class类对象 通过className取得具体Class对象
Class<?> cls=Class.forName(className);
//通过反射实例化对象
//因为newInstance返回的是Object,所以需要强转称为一个类型
study=(Study)cls.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return study;
}
}
public class FanSheFac {
public static void main(String[] args)
{
Study study1=Factory1.getStudy("CODE.反射.Pen");
study1.need(); //需要一直笔
Study study2=Factory1.getStudy("CODE.反射.Pencil");
study2.need(); //需要一只铅笔
}
}
反射与类
取得类的包名:
public Package getPackage() {
return Package.getPackage(this);
}
如:
import java.util.Date;
public class Fan {
public static void main(String[] args) {
Class<?> cls1=Date.class;
System.out.println(cls1.getPackage()); //package java.util, Java Platform API Specification, version 1.8
System.out.println(cls1.getPackage().getName()); //java.util 利用getName取得具体包名
}
}
1.反射取得父类、父接口信息
- 取得父类的Class对象:public native Class<? super T> getSuperclass();
- 取得父接口的Class对象:public Class<?>[ ] getInterfaces();(接口在JVM中也是Class因为接口有多继承,所以父接口可能有多个)
package CODE.反射;
//反射与类操作
//取得父类的Class对象
class A{}
interface IB{}
interface IC{}
class ABC extends A implements IB,IC{}
public class FanClass {
public static void main(String[] args) {
Class<?> cls=ABC.class; //获得Class对象
//获得父类Class对象
Class<?> cls1=cls.getSuperclass();
System.out.println("父类Class对象:"+cls1);
//父类Class对象:class CODE.反射.A
//获得父接口Class对象
Class<?>[] cls2=cls.getInterfaces();
for(Class<?> clas:cls2)
{
System.out.println("父接口Class对象:"+clas);
//父接口Class对象:interface CODE.反射.IB
//父接口Class对象:interface CODE.反射.IC
}
}
}
2.反射调用类中构造方法–Constructor类(描述类中构造方法)
一个类有多个构造,如果要想取得类中构造方法,可以使用Class类提供的2个方法:
- 取得指定参数类型的构造:
public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
只能取得类中public权限的指定构造方法
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
可以取得类中全部指定构造方法,包含私有构造
参数是类型的类对象,比如类型是String,那么参数是String.class。
- 取得类中所有构造方法
public Constructor<?>[] getConstructors() throws SecurityException
取得所有public权限构造方法
public Constructor<?>[] getDeclaredConstructors() throws SecurityException
取得所有权限构造方法(包括私有权限)
动态设置封装(只能通过反射调用)
Constructor /Method/Field类都AccessibleObjet的子类。
AccessibleObjet提供动态设置封装方法
public void setAccessible(boolean flag) throws SecurityException
setAccessible只能在本次JVM进程中有效,重新启动时,private权限方法依然不可以在外部使用,也就是说只是动态破坏了本次的封装。
Constructor提供了实例化对象的方法:
public T newInstance(Object … initargs) :可以调用类中其他有参构造。
Constructor类的newInstance(Object … initargs)与Class类的newInstance( )区别:
1.Constructor类的实例化对象方法参数有可变参数,即可以有参数,也可以没有参数,Class类提供的实例化对象方法必须是无参。
2.当有了Constructor对象后,也就是取得类的构造方法,假如构造方法是私有权限,可以利setAccessible(true)动态破坏私有构造用newInstance实例化对象,而Class类的newInstance只有用public权限构造方法来实例化对象。
代码如下:
package CODE.反射;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
反射与构造
class Student
{
private String name;
private int sno;
public Student()
{
}
private Student(String name, int sno) {
this.name = name;
this.sno = sno;
}
public String toString()
{
return "姓名:"+name+"学号"+sno;
}
}
public class FanConstruct {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<?> cls=Student.class;
Constructor<?> constructor=cls.getDeclaredConstructor(String.class,int.class);
constructor.setAccessible(true);
//Constructor类提供的newInstance()方法实例化对象 ,参数是要设置的值
//可以通过Constructor类提供的setAccessible使动态破坏权限,设置为true后,private权限可以在外部使用
//但是也是在在本次JVM进程有效,重新启动后依然是private权限
System.out.println(constructor.newInstance("pick",10)); //姓名:pick学号10
}
}
3.反射调用类中普通方法-- - -Method(描述类中普通方法)
- 描述类中指定名称的普通方法
public Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
取得本类和父类中public权限指定方法
public Method getDeclaredMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
取得本类任何权限的指定方法(私有、公有、继承权限)
参数中String nama :方法名
parameterTypes:参数类型的类对象,如String.class ,既要传方法名,又要传类型是为了重载。
- 取得类中全部普通方法
public Method[] getMethods() throws SecurityException
取得本类和父类中所有public权限方法
public Method[] getDeclaredMethods() throws SecurityException
取得本类中所有权限方法(注意只有本类)
例:
package CODE.反射;
import java.lang.reflect.Method;
class Person1
{
public void print1(){}
private void print2(){}
public void print3(String str){}
}
class Student1 extends Person1
{
public void print2(int m){
}
private void print3(int a){}
}
public class FanSheMeth {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException {
Class<?> cls=Student1.class;
Student1 student1=(Student1)cls.newInstance(); //通过反射取得实例化对象
Method method1[]=cls.getDeclaredMethods();
Method method2[]=cls.getMethods();
Method method3=cls.getMethod("print3",String.class);
Method method4=cls.getDeclaredMethod("print3", int.class);
System.out.println("getDeclaredMethods取得本类中所有权限方法:");
for(Method me1:method1)
{
System.out.println(me1);
}
System.out.println("getMethods取得本类和父类中所有public权限方法");
for(Method me2:method2)
{
System.out.println(me2);
}
System.out.println("getMethod取得本类和父类所有public权限指定方法:");
System.out.println(method3);
System.out.println("getDeclaredMethod取得本类中public权限指定方法");
System.out.println(method4);
}
}
Method类中提供调用类中普通方法的API:
public Object invoke(Object obj, Object… args)
如:
package CODE.反射;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
class Person
{
private String name;
private int age;
public void print(String name,int age)
{
this.name=name;
this.age=age;
System.out.println("name:"+this.name+" age:"+this.age);
}
private void print(String str)
{
System.out.println(str);
}
}
public class Meth
{
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<?> cls=Person.class;
Person person=(Person)cls.newInstance();
Method printMeth=cls.getMethod("print",String.class,int.class);
printMeth.invoke(person,"pick",10); //name:pick age:10
//Method对象调set
Method printStr=cls.getDeclaredMethod("print", String.class); //取得本类本任意权限方法
printStr.setAccessible(true); //动态破坏权限,使私有权限可见
printStr.invoke(person,"nice day"); //nice day
}
}
4.反射调用类中属性----Field(描述类中普通属性)
- 取得类中指定属性
public Field getField(String name) throws NoSuchFieldException, SecurityException
取得本类和父类中public指定权限属性
public Field getDeclaredField(String name)
throws NoSuchFieldException, SecurityException
取得本类任意权限属性(私有、公有、继承、默认)
- 取得类中所有属性
public Field[] getFields() throws SecurityException
取得本类以及父类中所有public属性
public Field[] getDeclaredFields() throws SecurityException
取得本类中全部普通属性(包括私有属性)
Field类提供设置与取得属性:
设置属性:
public void set(Object obj,Object value);
obj:实例化对象,value:具体的值
取得属性:
pulic Object get(Object obj);
取得属性的类型:
pubic Class<?> getType
对上述方法进行验证和练习:
package CODE.反射;
import com.sun.corba.se.impl.orbutil.concurrent.Sync;
import java.lang.reflect.Field;
class Person2
{
private String name;
private int age;
public int count=10;
}
class Student2 extends Person2
{
public int num=1;
private String sno; //课程号
}
public class FanSheField {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchFieldException {
Class<?> cls=Student2.class;
Student2 student2=(Student2)cls.newInstance();
System.out.println("getField取得本类和父类中public权限属性:");
Field count=cls.getField("count");
count.set(student2,1); //设置值
System.out.println("Field取得本类公有属性值: "+count+":"+count.get(student2));
System.out.println("getDeclaredField取得本类任意权限属性");
Field sno=cls.getDeclaredField("sno");
sno.setAccessible(true); //破坏私有权限
sno.set(student2,"3");
System.out.println("Field取得本类私有权限属性值: "+sno+":"+sno.get(student2));
System.out.println("取得属性类型:"+sno.getType());
System.out.println("getFields取得本类和父类所有公有权限属性");
Field field1[]=cls.getFields();
for(Field f1: field1)
{
System.out.println(f1);
}
System.out.println("getDeclaredFields取得本类所有权限属性");
Field field2[]=cls.getDeclaredFields();
for(Field f2:field2)
{
System.out.println(f2);
}
}
}
反射应用:
实现任意类属性设置。
如果希望对一个类的属性进行设置,当输入字符串是"emp.name:pick | emp.job:cook"时,emp是真实类中属性,name和job是类中属性名称,“pick"和"cook"是要设置的值,两个属性用”|"进行分割,如果可以将属性设置为此种格式,可以一次性将属性设置完毕。
代码如下:
emp是真实类,EmpAction是面向用户的类,BeanOperation是公共操作类(任意类通过这个类来设置属性):
Emp.java
package CODE.反射与简单Java类;
public class Emp {
private String name;
private String job;
public String getName() {
return name;
}
public String getJob() {
return job;
}
public void setName(String name) {
this.name = name;
}
public void setJob(String job) {
this.job = job;
}
public String toString()
{
if(job==null)
return "name :"+name;
return "job:"+job;
}
}
EmpAction.java
package CODE.反射与简单Java类;
import java.lang.reflect.InvocationTargetException;
public class EmpAction {
private Emp emp=new Emp();
public void setValue(String value) throws Exception {
BeanOperation.setBeanValue(this,value);
}
public Emp getEmp()
{
return emp;
}
}
BeanOperation .java :
package CODE.反射与简单Java类;
//
import javax.management.ObjectName;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
public class BeanOperation {
public static void setBeanValue(Object ActionObj ,String str) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// emp.name:pick|emp.job:cooker
String tmp[]=str.split("\\|");
//emp.name:pick
//emp.jon:cooker
for(int i=0;i<tmp.length;i++)
{
String msg[]=tmp[i].split(":");
String attribute=msg[0]; //属性前缀 emp.name
String value=msg[1]; //属性值 pick
//取得真实类
String realClassName=attribute.split("\\.")[0]; //emp
String attrValue=attribute.split("\\.")[1]; //name
Object realClass=getRealClass(ActionObj,realClassName);
//获得真实类后setValue
setValue(realClass,attrValue,value);
System.out.println(realClass);
}
}
public static Object getRealClass(Object ActionObj,String realClassName ) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Object obj=null;
Class<?> actionCls=ActionObj.getClass(); //类对象
String methName="get"+upFirst(realClassName); //取得要访问的方法名称
Method method=actionCls.getMethod(methName); //通过反射获得方法
obj=method.invoke(ActionObj); //调用获得方法
return obj;
}
public static String upFirst(String str)
{
return str.substring(0,1).toUpperCase()+str.substring(1);
}
public static void setValue(Object realclass,String arrtrValue,String value) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<?> cls=realclass.getClass();
String MethodName="set"+upFirst(arrtrValue);
Method setMethod=cls.getMethod(MethodName,String.class);
setMethod.invoke(realclass,value);
}
}
Test类:
package CODE.反射与简单Java类;
import java.lang.reflect.InvocationTargetException;
public class Test {
public static void main(String[] args) throws Exception {
EmpAction empAction=new EmpAction();
empAction.setValue("emp.name:pick|emp.job:cook");
}
}
类加载器
什么是类加载器:
类加载器:
通过一个类的全名称来获取此类的二进制字节流,实现这个操作的代码模块为类加载器。
通过 Class类的getClassLoader可以获取到类加载器:
public ClassLoader getClassLoader() ;
编写一个简单的反射程序,来观察ClassLoader的存在:
package CODE.反射;
public class Loader {
public static void main(String[] args) {
Class<?> cls=Loader.class;
System.out.println(cls.getClassLoader()); //sun.misc.Launcher$AppClassLoader@18b4aac2
System.out.println(cls.getClassLoader().getParent());//sun.misc.Launcher$ExtClassLoader@4554617c
System.out.println(cls.getClassLoader().getParent().getParent()); //null
}
}
可以看到有2个类加载器:ExtClassLoader和AppClassLoader。
实际 上在JDK中内置有3大类加载器。
3大类加载器的关系:
1.Bootstarp(启动类加载器):
- 使用c++实现,是JVM的一部分,(无法用,所以上述代码为空),其他所有类加载器均使用Java实现。
- 负责将存放于Java_HOME\lib目录下的能被JVM识别的类库(tr.jar—存放了Java所有基础类库,java.lang,java.util)加载到JVM中
- 启动类加载器无法被Java程序直接引用
如Object包由Bootstarp加载。
2.ExtClassLoader(扩展类加载器)
- 使用Java实现,并且可以被Java程序直接引用
- 加载Java_Home\lib\ext目录下能被识别的类库。
3.AppClassLoader(应用程序类加载器)
- 负责加载用户路径(classPath)上指定的类库
- 如果用户程序中没有自定义类加载器,则此加载器就是Java程序中默认的类加载器。
双亲委派模型:
双亲委派模型的工作流程是:如果一个类加载器收到了类加载请求,它首先不会自己去尝试加载这个类,而是把这
个请求委托给父类加载器去完成,每一个层次的类加载器都是如此。因此,所有的加载请求都应当传送到顶层的BootStrap加载器中,只有当父加载器反馈无法完成这个加载请求时(在自己搜索范围中没有找到此类),子加载器才会尝试自己去加载。
双亲委派模型的意义:
假如自定义java.lang.Object类,会因为双亲委派模型由Bootstarp加载器加载,Bootstarp加载的是Java自定义的java.lang.Object,那么自定义的java.lang.Object类就不会加载。