zoukankan      html  css  js  c++  java
  • 类型信息

    一、Class对象

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

    public final Class getClass()

        @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
        }


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

     

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

            // 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)向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。

    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对象的参数信息。

    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)源码分析

  • 相关阅读:
    PPT能输英文不能输汉字
    常用HTML正则表达式
    Log4j使用总结
    JsonConfig过滤对象属性
    打开”我的电脑“,不显示”共享文档“和”我的文档“,解决办法。(windows XP系统)
    错误org.hibernate.LazyInitializationException
    Tomcat中实现IP访问限制
    windows server 2008中让AD域中的普通用户可以 远程登录 域控服务器。
    ibatis简介及 like查询
    IE访问页面的时候,受限制的解决方案。
  • 原文地址:https://www.cnblogs.com/aidata/p/12036710.html
Copyright © 2011-2022 走看看