zoukankan      html  css  js  c++  java
  • 廖雪峰Java4反射与泛型-1反射-1Class类

    1.Class类与反射定义

    Class类本身是一种数据类型(Type),class/interface的数据类型是Class,JVM为每个加载的class创建了唯一的Class实例。
    Class实例包含该class的所有信息,通过Class实例获取class的信息的方法称为反射(Reflection)

    Java除基本类型外,其他都是class(包括interface)。如String、Object、Runnable、Exception...

    • class(包括interface)的本质是数据类型。我们把一个对象实例赋值给一种数据类型变量时,必须严格按照数据类型赋值。
    • 没有继承关系的数据类型是无法赋值的。Java定义了一种强数据类型关系,并且编译器会检查数据类型,不符合数据类型要求的,在赋值时,编译器就会报错。
            String s = new String("Hello");
            Object o = new Double(123.456);
            Runnable r = new Thread();
            Exception e = new RuntimeException();       
    

    class/interface的数据类型是Class。
    每加载一个class,JVM就为其创建一个Class类型的实例,并关联起来。Class实例是JVM内部创建的。

    JVM持有的每个Class实例都指向一个数据类型(class或interface)

    Class实例
    name java.lang.String --> String
    name java.util.Randome --> Random
    name java.lang.Runnable --> Runnable

    一个Class实例包含了该class的完整信息。如String的Class实例持有name,package,super,interface,field,method

    Class实例
    name java.lang.String
    package java.lang
    super java.lang.Object
    interface CharSequence, ...
    field value[], hash, ...
    method indexOf, valueOf, ...

    所以Java定义的Class类是:

    • JVM为每个加载的class创建对应的Class实例,并在实例中保存该class的所有信息
    • 如果获取了某个Class实例,则可以获取到该实例对应的class的所有信息。
    • 通过Class实例获取class信息的方法成为反射(Reflection )

    2.获取一个class的Class实例

    方法1:通过Type.class

            Class cls = String.class;
    

    方法2:通过实例getClass()

            String s = "Hello";
            Class cls1 = s.getClass();
            Class cls2 = "Hello".getClass();
    

    方法3:Class.forName(完整类名)

            try{
                Class cls = Class.forName("java.lang.String");
            }catch (Exception e){
                System.out.println(e);
            }
    

    Class实例在JVM中是唯一的:

    • 因此可以用 == 来比较两个Class实例
    public class Main {
        public static void main(String[] args){
            Class cls1 = String.class;
    
            String s = "Hello";
            Class cls2 = s.getClass();
            System.out.println("cls1 == cls2: " + (cls1 == cls2));
    
            Class cls3;
            try{
                cls3 = Class.forName("java.lang.String");
                System.out.println("cls2 == cls3: " + (cls2 == cls3));
            }catch (ClassNotFoundException e){
                System.out.println(e);
            }
        }
    }
    

    2.1Class实例比较和instanceof的差别

    public class Main {
        public static void main(String[] args){
            Integer n = new Integer(2);
            boolean b1 = n.getClass()==Integer.class;
            System.out.println("n.getClass()==Integer.class: " + b1);
            boolean b2 = n.getClass()==Number.class;
            //System.out.println("n.getClass() == Number.class: "+ b2);//Error:(10, 34) java: 不可比较的类型: java.lang.Class<capture#1, 共 ? extends java.lang.Integer>和java.lang.Class<java.lang.Number>
    
            boolean b3 = n instanceof Integer;
            System.out.println("n instanceof Integer: " + b3);
            boolean b4 = n instanceof Number;
            System.out.println("n instanceof Number: " + b4);
        }
    
    通常情况下,使用instanceof判断数据类型,因为面向抽象编程的时候,我们不关心具体的子类型;只有在需要精确判断某个类型的时候,才需要获取其Class进行判断。 ## 2.2反射的目的 反射的目的是当获得某个Object实例时,我们可以获取该Object的class信息。 从Class实例获取class信息: * getName() 获取完整的类名 * getSimpleName() 获取简单的类名 * getPackage() 获取包名 ```#java public class Main { public static void main(String[] args){ Integer n = new Integer(2); Class cls = n.getClass();
        String fullName = cls.getName();
        System.out.println(fullName);
        
        String simpleName = cls.getSimpleName();
        System.out.println(simpleName);
        
        String packageName = cls.getPackage().getName();
        System.out.println(packageName);
    }
    

    }

    <img src="https://img2018.cnblogs.com/blog/1418970/201902/1418970-20190203151635280-1262524766.png" width="500" />
    
    从Class实例判断class类型
    *    isInterface() 是否是接口
    *    isEnum()    是否是枚举类
    *    isArray()    是否是数组
    *    isPrimitive()    是否是基本类型</font>
    ```#java
    public class Main {
        public static void main(String[] args){
            Boolean b1 = Runnable.class.isInterface();
            Boolean b2 = java.time.Month.class.isEnum();
            Boolean b3 = String[].class.isArray();
            Boolean b4 = int.class.isPrimitive();
            Boolean b5 = String.class.isPrimitive();
    
            System.out.println("Runnable.class是接口:"+b1);
            System.out.println("java.time.Month.class是枚举累:"+b2);
            System.out.println("String[].class是数组:"+b3);
            System.out.println("int.class是基本类型:"+b4);
            System.out.println("String.class是基本类型:"+b5);
        }
    }
    
    ## 2.3通过newInstance创建新的实例。 ```#java public class Main { public static void main(String[] args) throws InstantiationException,IllegalAccessException{ Class cls = String.class; /*通过newInstance可以创建一个新的String实例。 局限:只能调用public的没有参数的构造方法,带参数的构造方法是不能调用的。*/ String s = (String) cls.newInstance(); } } ``` # 3.动态加载 利用JVM动态加载class的特性: * 可以在运行期根据条件加载不同的实现类 ```#java public class Main { //如Commons Logging优先使用Log4j,没有再使用jdk自带的Logging public static void main(String[] args) { /* 代码仅供演示。createLog4j()和createJdkLog()未定义 */ LogFactory factory; if (isClassPresent("com.apache.Logging.log4j.Logger")){ factory = createLog4j(); }else { factory = createJdkLog(); } } static boolean isClassPresent(String name){ try{ Class.forName(name);//判断一个类是否存在 return true; }catch (Exception e){ return false; } } } ``` Hello.java ```#java package com.reflection;

    public interface Hello {
    public void hello();
    }

    Student.java
    ```#java
    package com.reflection;
    
    public class Student implements Hello{
        private String name;
        public Student(){
            this("unNamed");
        }
        public Student(String name){
            this.name=name;
        }
        public void hello(){
            System.out.println(name+" is Student");
        }
    }
    

    Teacher.java

    package com.reflection;
    
    public class Teacher implements Hello{
        private String name;
        public Teacher(){
            this("unNamed");
        }
        public Teacher(String name){
            this.name=name;
        }
        public void hello(){
            System.out.println(name+" is Teacher");
        }
    }
    

    Main.java

    package com.reflection;
    
    public class Main {
        public static void main(String[] args) throws InstantiationException,IllegalAccessException{
            Class cls = Student.class;
            System.out.println("class name:"+cls.getName());
            System.out.println("class simple name:"+cls.getSimpleName());
            System.out.println("class package name:"+cls.getPackage().getName());
            Student s = (Student) cls.newInstance();
            s.hello();
        }
    }
    
    改写Main.java,使用动态加载实现 ```#java package com.reflection;

    public class Main {
    public static void main(String[] args) throws InstantiationException,IllegalAccessException,ClassNotFoundException{
    Class cls ;
    if (clsClassPresent("com.reflection.President")){
    cls = Class.forName("com.reflection.President");
    }else{
    cls = Class.forName("com.reflection.Student");
    }
    System.out.println("class name:"+cls.getName());
    System.out.println("class simple name:"+cls.getSimpleName());
    System.out.println("class package name:"+cls.getPackage().getName());
    Hello s = (Hello) cls.newInstance();
    s.hello();
    }
    static boolean clsClassPresent(String name){
    try{
    Class.forName(name);
    return true;
    }catch (Exception e){
    return false;
    }
    }
    }

    <img src="https://img2018.cnblogs.com/blog/1418970/201902/1418970-20190211211313954-898023001.png" width="500" />
    
    
    #    4.总结
    *    JVM为每个加载的class创建对应的Class实例来保存class的所有的信息
    *    获取一个class的Class实例后,就可以获取该class的所有信息
    *    通过Class实例获取class信息的方法成为反射Reflection
    *    JVM总是动态加载class,可以在运行期根据条件控制加载class
  • 相关阅读:
    C# 项目提交过程中感受
    C# 工作中遇到的几个问题
    C# Enum Name String Description之间的相互转换
    Win 10下安装 Redis
    Entity Framework 学习系列(3)
    Entity Framework 学习系列(2)
    Entity Framework 学习系列(1)
    解决 win10 家庭版环境下 MySQL 的ODBC驱动下载及安装
    Echarts 学习系列(3)-Echarts动态数据交互
    Echarts 学习系列(2)-常见的静态ECharts图
  • 原文地址:https://www.cnblogs.com/csj2018/p/10345863.html
Copyright © 2011-2022 走看看