4Java反射与泛型——1反射——1class类(廖雪峰)

1class

Java除基本类型外其他都是class(包括interface)

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

String s = new Double(123.456);//报错
  • class/interface的数据类型就是Class
  • 每加载一个class,JVM就为其创建一个Class类型的实例,并关联起来,Class实例是JVM内部创建的。

public final class Class {
    private Class() {}
}
  •  JVM持有的每个Class实例都指向一个数据类型(class或interface)
class Class{

}

 

  • 一个Class实例包含了该class的完整信息

例如String类的class

所以Java定义的Class类是:

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

在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着这个对象所属的类。可以通过专门的Java类访问这些信息,保存这些信息的类被称为Class类

如何获取一个class的Class实例?

  • Type.class
  • getClass()通过调用Object类中的getClass()方法返回一个Class对象
  • Class.forName(完整类名)在使用Class.forName()方法时,必须提供一个类的全名,这个全名包括类所在的包的名字。如果在调用该方法时,没有找到该路径的类,将会抛出ClassNotFoundException。
//方法1 (类名)Type.class
Class cls = String.class;

//方法2 如果有实例变量,通过getClass()来获取
String s = "Hello";
Class cls = s.getClass();

//方法3 通过Class.forName(),传入完整的类名获取
Class cls = Class.forName("java.lang.String");

Class实例在JVM中是唯一的,一个JVM只对应唯一的Class实例

  • 可以用==比较两个Class实例
boolean b1 = cls1 == cls2; //true
boolean b2 = cls2 == cls3; //true
//用三种方法任意获取的class实例都指向同一个class对象,所以是true

Class实例比较和instanceof的差别:

Integer n = new Integer(123);

boolean b3 = n instanceof Integer; // true
boolean b4 = n instanceof Number; // true

boolean b1 = n.getClass() == Integer.class; // true
boolean b2 = n.getClass() == Number.class; // false

iinstanceof不但匹配当前类型,还匹配当前类型的子类,因为Integer为Number的子类,所以为true

但是用“==”判断class实例时只能精确的判断数据类型,不能做子类的比较

通常情况下,使用instanceof判断数据类型,因为面向抽象编程的时候,我们不关心具体的子类型;只有在需要精确判断某个类型是否是某个Class的时候,我们才是用“==“来判断

2Reflection

反射的目的是当获取某个Object实例时,我们可以获取该Object的class信息

从Class实例获取class信息:

  • getName() 取完整的类名
  • getSimpleName() 获取简单的类名
  • getPackage() 获取包名
Class cls = String.class;

String fname = cls.getName(); // "java.lang.String";
String sname = cls.getSimpleName(); // "String";
String pkg = cls.getPackage().getName(); // "java.lang";

从Class实例判断class类型 :

  • isInterface() 是否是接口类型
  • isEnum() 是否是枚举类类型 
  • isArray() 是否是数组类型 
  • isPrimitive() 是否是基本类型
Runnable.class.isIntegerface(); // true

java.time.Month.class.isEnum(); // true

String[].class.isArray(); // true

int.class.isPrimitive(); // true

创建class实例:

  • newInstance()
Class cls = String.class;

// new String():
String s = (String) cls.newInstance();

3动态加载

利用JVM动态加载的特性:

  • 可以在运行期根据条件加载不同的实现类
boolean isClassPresent (String name) {
    try {
        Class.forName(name);
    } catch (Exception e) {
        return false;
    }
}

利用JVM动态加载class的特性,可以在运行期根据条件加载不同的实现类

//Commons Logging 优先使用Log4j:
LogFactory factory;
if (isClassPresent ("org.apache.logging.log4j.Logger")){
    factory = createLog4j();
} else {
    factory = createJdkLog();
}

boolean isClassPresent (String name) {
    try {
        Class.forName(name);
        return true;
    } catch (Exception e){
        return false;
    }
}

Hello.java(接口)

public interface Hello {
	public void hello();
}

Student.java

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 Studnet");
    }
}

Teacher.java

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

public class Main {
	
    public static void main(String[] args) throws Exception{
	Class cls = Student.class;
	System.out.println("class name:" + cls.getName());
	System.out.println("class simple name: " +cls.getSimpleName());
	System.out.println("package name" +cls.getPackage().getName());
	System.out.println("is interface?" + cls.newInstance());
	//Student s = new Student();
	Student s = (Student) cls.newInstance();
	s.hello();
    }
}

对代码进行修改,运行结果不变

public class Main {
	
    public static void main(String[] args) throws Exception{
        Class cls;
	if(true) {
            cls = Class.forName("com.xiaoxiatianxi.sample.Student");
        }else {
	    cls = Class.forName("com.xiaoxiatianxi.sample.Teacher");
	}
        System.out.println("class name:" + cls.getName());
	System.out.println("class simple name: " +cls.getSimpleName());
	System.out.println("package name" +cls.getPackage().getName());
	System.out.println("is interface?" + cls.newInstance());
	//Student s = new Student();
	Hello s = (Hello)cls.newInstance();
	s.hello();
    }
}

4总结

  • JVM为每个加载的class创建对应的Class实例来保存class的所有的信息 
  • 获取一个class的Class实例后,就可以获取该class的所有信息 
  • 通过Class实例获取class信息的方法成为反射Reflection
  • JVM总是动态加载class,可以在运行期根据条件控制加载class

猜你喜欢

转载自blog.csdn.net/qq_24573381/article/details/107762674