类型信息

摘要:
在每个运行时类仅加载一次具有class的实例后,创建相应的运行时类对象以获得相应运行时类的完整结构(属性、方法、构造函数、父类、包、异常、注释等),调用相应运行时的指定结构(属性,方法、构造函数),并反映应用程序:它可以通过Class类的静态方法forName()获得,4)其他方法(不需要)ClassLoadercl=this。getClass()。getClassLoader();

一、Class对象

在Object类中定义了以下的方法,此方法将被所有子类继承:

public final Class getClass()

类型信息第1张

    @Test
    public void test3() {
        Person person = new Person();
        Class clazz = person.getClass();
        System.out.println(clazz);
    }

结果

得到完整的“包类”名称

class com.aidata.clazz.Person

以上的getClass()方法返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称。

创建一个类,通过编译(javac.exe),生成对应的.class文件,之后使用java.exe加载(JVM的类加载器)。此.class文件加载到内存以后,就是一个运行时类,存到缓存区中。这个运行时类本身就是一个Class的实例。

每一个运行时类只加载一次

有了Class的实例以后,可以进行如下操作:

  • 创建对应的运行时类对象
  • 获取对应的运行时类的完整结构(属性、方法、构造器、父类、所在的包、异常、注解......)
  • 调用对应的运行时类的指定的结构(属性、方法、构造器)
  • 反射的应用:动态代理

获取Class的实例

1)前提:若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高
实例:Class clazz = String.class;


2)前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象
实例:Class clazz = “www.atguigu.com”.getClass();


3)前提:已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException
实例:Class clazz = Class.forName(“java.lang.String”);


4)其他方式(不做要求)
ClassLoader cl = this.getClass().getClassLoader();
Class clazz4 = cl.loadClass(“类的全类名”);

//如何获取Class的实例(3种)
    @Test
    public void test4() throws ClassNotFoundException{
        //1.调用运行时类本身的.class属性
        Class clazz1 = Person.class;
        System.out.println(clazz1.getName());
        
        Class clazz2 = String.class;
        System.out.println(clazz2.getName());
        
        //2.通过运行时类的对象获取
        Person p = new Person();
        Class clazz3 = p.getClass();
        System.out.println(clazz3.getName());
        
        //3.通过Class的静态方法获取.通过此方式,体会一下,反射的动态性。
        String className = "com.atguigu.java.Person";
        Class clazz4 = Class.forName(className);
//        clazz4.newInstance();
        System.out.println(clazz4.getName());
        
        //4.(了解)通过类的加载器
        ClassLoader classLoader = this.getClass().getClassLoader();
        Class clazz5 = classLoader.loadClass(className);
        System.out.println(clazz5.getName());
        
        System.out.println(clazz1 == clazz3);//true
        System.out.println(clazz1 == clazz4);//true
        System.out.println(clazz1 == clazz5);//true
    }


当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。

类型信息第2张

 类型信息第3张

类加载器是用来把类(class)装载进内存的。JVM 规范定义了两种类型的类加载器:启动类加载器(bootstrap)和用户自定义加载器(user-defined class loader)。 JVM在运行时会产生3个类加载器组成的初始化加载器层次结构 ,如下图所示:

类型信息第4张

        // 1.获取一个系统类加载器
        ClassLoader classloader = ClassLoader.getSystemClassLoader();
        System.out.println(classloader); // sun.misc.Launcher$AppClassLoader@45ee12a7
        // 2.获取系统类加载器的父类加载器,即扩展类加载器
        classloader = classloader.getParent();
        System.out.println(classloader); // sun.misc.Launcher$ExtClassLoader@6ae40994
        // 3.获取扩展类加载器的父类加载器,即引导类加载器
        classloader = classloader.getParent();
        System.out.println(classloader); // null,无法获取引导类加载器
        // 4.测试当前类由哪个类加载器进行加载
        classloader = Class.forName("com.aidata.clazz.TestReflection").getClassLoader();
        System.out.println(classloader); // sun.misc.Launcher$AppClassLoader@45ee12a7
        // 5.测试JDK提供的Object类由哪个类加载器加载
        classloader = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(classloader); // null
        // 6.关于类加载器的一个主要方法:getResourceAsStream(String str):获取类路径下的指定文件的输入流
        InputStream in = null;
        in = this.getClass().getClassLoader().getResourceAsStream("exer2\\test.properties");
        System.out.println(in);

二、反射

Reflection是动态语言的关键,反射机制允许程序在执行期间借助Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

数据库操作会用到反射:

数据库中有customer、student两张表

转为Java对象才能用Java进行操作,前提是创建Java对象

因为我们操作是不确定的,可能只操作customer,也可能只操作student,也可能两者都操作

很自然的想法就是在真正操作对应表格的时候创建对应对象

我们不想为每一个表都创建一个方法,想实现一个通用的方法,可以对任何表进行处理

该方法会根据是cusomer还是student表创建不同的对象,创建的对象没有在源代码中写死

Class类与java.lang.reflect类库一起对反射的概念进行了支持:

  • java.lang.Class:代表一个类
  • java.lang.reflect.Method:代表类的方法
  • java.lang.reflect.Field:代表类的成员变量
  • java.lang.reflect.Constructor:代表类的构造方法
  • ...
// 在有反射之前,创建一个类的对象,并调用其中的方法、属性
    @Test
    public void test1() {
        Person person = new Person();
        person.setAge(10);
        person.setName("Tom");
        System.out.println(person);
        person.show();
        person.display("US");
    }

    // 有了反射可以使用反射创建一个类的对象,并调用其中的结构
    @Test
    public void test2() throws Exception {
        Class clazz = Person.class;
        // 1.创建clazz对应的运行时类Person类的对象
        Person person = (Person) clazz.newInstance();
        // 使用泛型可以不用强转
        // Class<Person> clazz = Person.class;
        // Person person = clazz.newInstance();
        System.out.println(person);
        // 2.调用属性
        Field f1 = clazz.getField("name");
        f1.set(person, "Jack");
        System.out.println(person);
        // 修改private属性
        Field f2 = clazz.getDeclaredField("age");
        f2.setAccessible(true);
        f2.set(person, 20);
        System.out.println(person);

        // 3.通过反射调用运行时类的指定方法
        Method m1 = clazz.getMethod("show");
        m1.invoke(person);
        Method m2 = clazz.getMethod("display", String.class);
        m2.invoke(person, "CN");
    }

2.1 创建运行时类对象

调用Class对象的newInstance()方法
要 求:  1)类必须有一个无参数的构造器
              2)类的构造器的访问权限需要足够。

难道没有无参的构造器就不能创建对象了吗?
不是!只要在操作的时候明确的调用类中的构造方法,并将参数传递进去之后,才可以实例化操作。步骤如下:
1)通过Class类的getDeclaredConstructor(Class … parameterTypes)取得本类的指定形参类型的构造器
2)向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。

类型信息第5张

public class TestConstructor {
    @Test
    public void test1() throws Exception{
        String className = "com.atguigu.java.Person";
        Class clazz = Class.forName(className);
        //创建对应的运行时类的对象。使用newInstance(),实际上就是调用了运行时类的空参的构造器。
        //要想能够创建成功:①要求对应的运行时类要有空参的构造器。②构造器的权限修饰符的权限要足够。
        Object obj = clazz.newInstance();
        Person p = (Person)obj;
        System.out.println(p);
    }
    
    @Test
    public void test2() throws ClassNotFoundException{
        String className = "com.atguigu.java.Person";
        Class clazz = Class.forName(className);
        
        Constructor[] cons = clazz.getDeclaredConstructors();
        for(Constructor c : cons){
            System.out.println(c);
        }
    }
    
    //调用指定的构造器,创建运行时类的对象
    @Test
    public void test3() throws Exception{
        String className = "com.atguigu.java.Person";
        Class clazz = Class.forName(className);
        
        Constructor cons = clazz.getDeclaredConstructor(String.class,int.class);
        cons.setAccessible(true);
        Person p = (Person)cons.newInstance("罗伟",20);
        System.out.println(p);
    }
}
//1.根据全类名获取对应的Class对象
String name = “atguigu.java.Person";
Class clazz = null;
clazz = Class.forName(name);
//2.调用指定参数结构的构造器,生成Constructor的实例
Constructor con = clazz.getConstructor(String.class,Integer.class);
//3.通过Constructor的实例创建对应类的对象,并初始化类属性
Person p2 = (Person)con.newInstance("Peter",20);
System.out.println(p2);

2.2 调用类的完整结构

Field、Method、Constructor、Superclass、Interface、Annotation

  • 实现的全部接口
  • 所继承的父类
  • 全部的构造器
  • 全部的方法
  • 全部的Field

使用反射可以取得:
1.实现的全部接口
public Class<?>[] getInterfaces()
确定此对象所表示的类或接口实现的接口。

2.所继承的父类
public Class<? Super T> getSuperclass()
返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的 Class。

3.全部的构造器
public Constructor<T>[] getConstructors()
返回此 Class 对象所表示的类的所有public构造方法。
public Constructor<T>[] getDeclaredConstructors()
返回此 Class 对象表示的类声明的所有构造方法。

Constructor类中:
取得修饰符: public int getModifiers();
取得方法名称: public String getName();
取得参数的类型:public Class<?>[] getParameterTypes();

public class TestConstructor {
    @Test
    public void test1() throws Exception{
        String className = "com.atguigu.java.Person";
        Class clazz = Class.forName(className);
        //创建对应的运行时类的对象。使用newInstance(),实际上就是调用了运行时类的空参的构造器。
        //要想能够创建成功:①要求对应的运行时类要有空参的构造器。②构造器的权限修饰符的权限要足够。
        Object obj = clazz.newInstance();
        Person p = (Person)obj;
        System.out.println(p);
    }
    
    @Test
    public void test2() throws ClassNotFoundException{
        String className = "com.atguigu.java.Person";
        Class clazz = Class.forName(className);
        
        Constructor[] cons = clazz.getDeclaredConstructors();
        for(Constructor c : cons){
            System.out.println(c);
        }
    }
    
    //调用指定的构造器,创建运行时类的对象
    @Test
    public void test3() throws Exception{
        String className = "com.atguigu.java.Person";
        Class clazz = Class.forName(className);
        
        Constructor cons = clazz.getDeclaredConstructor(String.class,int.class);
        cons.setAccessible(true);
        Person p = (Person)cons.newInstance("罗伟",20);
        System.out.println(p);
    }
}

4.全部的方法
public Method[] getDeclaredMethods()
返回此Class对象所表示的类或接口的全部方法
public Method[] getMethods()
返回此Class对象所表示的类或接口的public的方法

Method类中:
public Class<?> getReturnType()取得返回值类型
public Class<?>[] getParameterTypes()取得全部的参数
public int getModifiers()取得修饰符
public Class<?>[] getExceptionTypes()取得异常信息

    //1.获取运行时类的方法
    
    @Test
    public void test1(){
        Class clazz = Person.class;
        //1.getMethods():获取运行时类及其父类中所有的声明为public的方法
        Method[] m1 = clazz.getMethods();
        for(Method m : m1){
            System.out.println(m);
        }
        System.out.println();
        
        //2.getDeclaredMethods():获取运行时类本身声明的所有的方法
        Method[] m2 = clazz.getDeclaredMethods();
        for(Method m : m2){
            System.out.println(m);
        }
    }
    //注解 权限修饰符 返回值类型 方法名 形参列表 异常
    @Test
    public void test2(){
        Class clazz = Person.class;
        
        Method[] m2 = clazz.getDeclaredMethods();
        for(Method m : m2){
            //1.注解
            Annotation[] ann = m.getAnnotations();
            for(Annotation a : ann){
                System.out.println(a);
            }
            
            //2.权限修饰符
            String str = Modifier.toString(m.getModifiers());
            System.out.print(str + " ");
            //3.返回值类型
            Class returnType = m.getReturnType();
            System.out.print(returnType.getName() + " ");
            //4.方法名
            System.out.print(m.getName() + " ");
            
            //5.形参列表
            System.out.print("(");
            Class[] params = m.getParameterTypes();
            for(int i = 0;i < params.length;i++){
                System.out.print(params[i].getName() + " args-" + i + " ");
            }
            System.out.print(")");
            
            //6.异常类型
            Class[] exps = m.getExceptionTypes();
            if(exps.length != 0){
                System.out.print("throws ");
            }
            for(int i = 0;i < exps.length;i++){
                System.out.print(exps[i].getName() + " ");
            }
            System.out.println();
        }
    }

5.全部的Field
public Field[] getFields()
返回此Class对象所表示的类或接口的public的Field。
public Field[] getDeclaredFields()
返回此Class对象所表示的类或接口的全部Field。

Field方法中:
public int getModifiers() 以整数形式返回此Field的修饰符
public Class<?> getType() 得到Field的属性类型
public String getName() 返回Field的名称。

//获取对应的运行时类的属性
    @Test
    public void test1(){
        Class clazz = Person.class;
        //1.getFields():只能获取到运行时类中及其父类中声明为public的属性
        Field[] fields = clazz.getFields();
        for(int i = 0;i < fields.length;i++){
            System.out.println(fields[i]);
        }
        System.out.println();
        //2.getDeclaredFields():获取运行时类本身声明的所有的属性
        Field[] fields1 = clazz.getDeclaredFields();
        for(Field f : fields1){
            System.out.println(f.getName());
        }
    }
    //权限修饰符  变量类型 变量名
    //获取属性的各个部分的内容
    @Test
    public void test2(){
        Class clazz = Person.class;
        Field[] fields1 = clazz.getDeclaredFields();
        for(Field f : fields1){
            //1.获取每个属性的权限修饰符
            int i = f.getModifiers();
            String str1 = Modifier.toString(i);
            System.out.print(str1 + " ");
            //2.获取属性的类型
            Class type = f.getType();
            System.out.print(type.getName() + " ");
            //3.获取属性名
            System.out.print(f.getName());
            
            System.out.println();
        }
    }
    

6. Annotation相关

get Annotation(Class<T> annotationClass)
getDeclaredAnnotations()
7.泛型相关
获取父类泛型类型:Type getGenericSuperclass()
泛型类型:ParameterizedType
获取实际的泛型类型参数数组:getActualTypeArguments()

8.类所在的包 Package getPackage()

public class TestOthers {
    //6.获取注解
    @Test
    public void test6(){
        Class clazz = Person.class;
        Annotation[] anns = clazz.getAnnotations();
        for(Annotation a : anns){
            System.out.println(a);
        }
    }
    
    //5.获取所在的包
    @Test
    public void test5(){
        Class clazz = Person.class;
        Package pack = clazz.getPackage();
        System.out.println(pack);
    }
    
    //4.获取实现的接口
    @Test
    public void test4(){
        Class clazz = Person.class;
        Class[] interfaces = clazz.getInterfaces();
        for(Class i : interfaces){
            System.out.println(i);
        }
    }
    
    //3*.获取父类的泛型
    @Test
    public void test3(){
        Class clazz = Person.class;
        Type type1 = clazz.getGenericSuperclass();
        
        ParameterizedType param = (ParameterizedType)type1;
        Type[] ars = param.getActualTypeArguments();
        
        System.out.println(((Class)ars[0]).getName());
    }
    
    //2.获取带泛型的父类
    @Test
    public void test2(){
        Class clazz = Person.class;
        Type type1 = clazz.getGenericSuperclass();
        System.out.println(type1);
    }
    
    //1.获取运行时类的父类
    @Test
    public void test1(){
        Class clazz = Person.class;
        Class superClass = clazz.getSuperclass();
        System.out.println(superClass);
    }
}

2.3 通过反射调用类中的指定方法、指定属性

调用指定方法

通过反射,调用类中的方法,通过Method类完成。步骤:
1.通过Class类的getMethod(String name,Class…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。
2.之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。

类型信息第6张

Object invoke(Object obj, Object …  args)

说明:
1.Object 对应原方法的返回值,若原方法无返回值,此时返回null
2.若原方法若为静态方法,此时形参Object obj可为null
3.若原方法形参列表为空,则Object[] args为null
4.若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。

//调用运行时类中指定的方法
    @Test
    public void test3() throws Exception{
        Class clazz = Person.class;
        //getMethod(String methodName,Class ... params):获取运行时类中声明为public的指定的方法
        Method m1 = clazz.getMethod("show");
        Person p = (Person)clazz.newInstance();
        //调用指定的方法:Object invoke(Object obj,Object ... obj)
        Object returnVal = m1.invoke(p);//我是一个人
        System.out.println(returnVal);//null
        
        Method m2 = clazz.getMethod("toString");
        Object returnVal1 = m2.invoke(p);
        System.out.println(returnVal1);//Person [name=null, age=0]
        //对于运行时类中静态方法的调用
        Method m3 = clazz.getMethod("info");
        m3.invoke(Person.class);
        
        //getDeclaredMethod(String methodName,Class ... params):获取运行时类中声明了的指定的方法
        Method m4 = clazz.getDeclaredMethod("display",String.class,Integer.class);
        m4.setAccessible(true);
        Object value = m4.invoke(p,"CHN",10);//我的国籍是:CHN
        System.out.println(value);//10
    }

调用指定的属性

在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()和get()方法就可以完成设置和取得属性内容的操作。
public Field getField(String name) 返回此Class对象表示的类或接口的指定的public的Field。
public Field getDeclaredField(String name)返回此Class对象表示的类或接口的指定的Field。

在Field中:
public Object get(Object obj) 取得指定对象obj上此Field的属性内容
public void set(Object obj,Object value) 设置指定对象obj上此Field的属性内容

    //调用运行时类中指定的属性
    @Test
    public void test3() throws Exception{
        Class clazz = Person.class;
        //1.获取指定的属性
        //getField(String fieldName):获取运行时类中声明为public的指定属性名为fieldName的属性
        Field name = clazz.getField("name");
        //2.创建运行时类的对象 
        Person p = (Person)clazz.newInstance();
        System.out.println(p);
        //3.将运行时类的指定的属性赋值
        name.set(p,"Jerry");
        System.out.println(p);
        System.out.println("%"+name.get(p));
        
        System.out.println();
        //getDeclaredField(String fieldName):获取运行时类中指定的名为fieldName的属性
        Field age = clazz.getDeclaredField("age");
        //由于属性权限修饰符的限制,为了保证可以给属性赋值,需要在操作前使得此属性可被操作。
        age.setAccessible(true);
        age.set(p,10);
        System.out.println(p);
        
//        Field id = clazz.getField("id");
        
    }

注:在类中属性都设置为private的前提下,在使用set()和get()方法时,首先要使用Field类中的setAccessible(true)方法将需要操作的属性设置为可以被外部访问。
public void setAccessible(true)访问私有属性时,让这个属性可见。

三、动态代理

静态代理

//静态代理模式
//接口
interface ClothFactory{
    void productCloth();
}
//被代理类
class NikeClothFactory implements ClothFactory{

    @Override
    public void productCloth() {
        System.out.println("Nike工厂生产一批衣服");
    }    
}
//代理类
class ProxyFactory implements ClothFactory{
    ClothFactory cf;
    //创建代理类的对象时,实际传入一个被代理类的对象
    public ProxyFactory(ClothFactory cf){
        this.cf = cf;
    }
    
    @Override
    public void productCloth() {
        System.out.println("代理类开始执行,收代理费$1000");
        cf.productCloth();
    }
    
}

public class TestClothProduct {
    public static void main(String[] args) {
        NikeClothFactory nike = new NikeClothFactory();//创建被代理类的对象
        ProxyFactory proxy = new ProxyFactory(nike);//创建代理类的对象
        proxy.productCloth();
    }
}

静态代理代理类和接口是配对的,来一个新的接口需要重新创建代理类

动态创建代理类

interface Subject {

    void action();
}

class RealSubject implements Subject {

    @Override
    public void action() {
        System.out.println("我是被代理类");

    }
}

class MyInvocationHandler implements InvocationHandler {

    Object obj; // 实现了接口的被代理类的对象的声明
    // 给被代理类的对象实例化
    // 返回一个代理类的对象

    public Object blind(Object obj) {
        this.obj = obj;
        // this是实现了InvocationHandler接口的对象
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
    }

    // 当通过代理类的对象发起对被重写的方法的调用时,都会转为对如下的invoke方法的调用
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // method方法的返回值是returnVal
        Object returnVal = method.invoke(obj, args);
        return returnVal;
    }

}

public class TestProxy {

    public static void main(String[] args) {
        // 1.被代理类的对象
        RealSubject real = new RealSubject();
        // 2.创建了一个实现了InvacationHandler接口的类的对象
        MyInvocationHandler handler = new MyInvocationHandler();
        // 3.调用blind()方法,动态的返回一个同样实现了real所在类实现的接口Subject的代理类的对象
        Object obj = handler.blind(real);
        Subject sub = (Subject) obj; // 此时sub就是代理类的对象
        sub.action(); // 转到对InvacationHandler接口的实现类的invoke()方法的调用

        NikeClothFactory nike = new NikeClothFactory();
        ClothFactory proxyCloth = (ClothFactory) handler.blind(nike); // proxyCloth即为代理类对象
        proxyCloth.productCloth();
    }
}

源码分析

JAVA设计模式-动态代理(Proxy)源码分析

免责声明:文章转载自《类型信息》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Flex 布局教程WPF 附加属性的用法 (一)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

C#反射实例应用--------获取程序集信息和通过类名创建类实例

AppDomain.CurrentDomain.GetAssemblies();获取程序集,但是获取的只是已经加载的dll,引用的获取不到。 System.Reflection.Assembly.GetEntryAssembly().GetReferencedAssemblies(); 获取程序集,包括已经加载的和引用的未加载的dll, 但是获取的不是As...

解决jQuery多个版本,与其他js库冲突方法

jQuery多个版本或和其他js库冲突主要是常用的$符号的问题,这个问题 jquery早早就有给我们预留处理方法了,下面一起来看看解决办法。 1.同一页面jQuery多个版本或冲突解决方法。 <!DOCTYPE html> <html lang="en"> <head> <meta charset="...

Tornado web 框架

Tornado web 框架 其实很简单、深度应用一、简介     Tornado 是 FriendFeed 使用的可扩展的非阻塞式 web 服务器及其相关工具的开源版本。这个 Web 框架看起来有些像web.py 或者 Google 的 webapp,不过为了能有效利用非阻塞式服务器环境,这个 Web 框架还包含了一些相关有用工具及优化。     Tor...

performSelector

perfromSelector 底层源码地址:https://opensource.apple.com/tarballs/objc4/ 非延迟方法 - (id)performSelector:(SEL)sel { if (!sel) [self doesNotRecognizeSelector:sel]; return ((id(*)(id...

C#基本面试题

重写和重载 重写: 要求:(三大同)参数相同,方法名相同,返回值相同 关键字:基类函数用virtual修饰,派生类用override修饰 注意:不能重写非虚方法或静态方法 重载: 要求:在同一作用域,可以存在相同的函数名,不同参数列表的函数,这组函数称为重载函数 其他 WebAPI和MVC的区别 1、MVC主要用于建站,WebAPI主要用于构建http...

springboot配置rabbitmq的序列化反序列化格式

SpringBoot封装了rabbitmq中,发送对象和接收对象时,会统一将对象和消息互相转换 会用到MessageConverter转换接口 在发送消息时, 会将Object转换成Message Message createMessage(Object object, MessageProperties messageProperties) 接收消息...