一、Class类的使用
1、在面向对象的世界里,万事万物皆对象。
不过在java语言中,有两类事物不是面向对象的。它们分别是 java 中的基本数据类型(例如:int a = 5)和 java 中的静态成员(包括静态成员变量、静态成员方法)。其中,虽然普通数据类型不是对象,但是与之对应的包装类却是面向对象的,例如:int - Integer,boolean - Boolean 等。
2、类是谁的对象呢?
类是对象,类是 java.lang.Class 类的实例对象。(There is a class named Class)
假如有一个类 Person,那么可以用 new Person()来表示 Person 类的一个实例对象。
其实在 Java 中,Person 类本身也是一个类的实例对象,这个类就是 Class (java.lang.Class)。
也就是说,在 Java 中任何一个类都是 Class 的实例对象,只不过这些实例对象只能由 JVM 创建,可以理解为就是编译之后产生的一个个 .class 文件。
java.lang.Class的部分源码如下:
public final
class Class<T> implements java.io.Serializable,
java.lang.reflect.GenericDeclaration,
java.lang.reflect.Type,
java.lang.reflect.AnnotatedElement {
···
/*
* Constructor. Only the Java Virtual Machine creates Class
* 私有的构造函数,只有Java虚拟机可以创建Class
* objects.
*/
private Class() {}
···
}
Person 的实例对象可以用 new Person( )来表示,那么 Class 的实例对象应该如何表示呢?
以 Person 类为例,表示 Class 的实例对象一般有以下三种方式:
a、类名.class
Class c1 = Person.class;
实际在告诉我们任何一个类都有一个隐含的静态成员变量 class
b、该类的已知对象.getClass()
Person p = new Person();
Class c2 = p.getClass();
小结:
这里 p 代表 Person 类的一个实例对象, c1 、c2 则分别代表 Class 类的实例对象。
官网描述为:c1、c2 表示了 Person 类的类类型(class type)。
万事万物皆对象,类也是对象,是Class类的实例对象,这个对象我们称为该类的类类型。
c1、c2都代表了 Person 类的类类型,c1 == c2 为true说明一个类只可能是 Class 类的一个实例对象。
c、Class.forName(“类的完整包名路径”)
try {
Class<?> c3 = Class.forName("com.example.Person");
System.out.println(c3);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
我们完全可以通过类的类类型来创建该类的对象实例。
try {
Person person = c1.newInstance(); // 【注意】需要有无参数的构造方法
person.print();
} catch (Exception e) {
e.printStackTrace();
}
完整的代码:
public class MainClass {
public static void main(String[] args) {
// Person是一个类,它的实例对象如何表示?
Person p = new Person(); // p 就代表了Person类的一个实例对象
// 其实,Person类也是一个类的实例对象,这个类就是 Class (java.lang.Class)
// 在Java中,任何一个类都是Class的实例对象,只不过这些实例对象只能由JVM创建,也就是编译后产生的一个个 .class 文件
// 那么Person作为Class的一个实例对象,应该如何来表示呢?
// 第一种方式
Class<? extends Person> c1 = p.getClass();
System.out.println(c1);
System.out.println(c1.getSuperclass()); // 这里印证了Class的父类其实还是Object
// 第二种方式
Class<? extends Person> c2 = Person.class; // 任何一个类都有一个隐含的静态成员变量class
System.out.println(c2);
System.out.println(c1 == c2);
// 第三种方式
try {
Class<?> c3 = Class.forName("com.example.Person");
System.out.println(c3);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 通过类的类类型来创建该类的对象实例
try {
Person person = c1.newInstance(); // 【注意】需要有无参数的构造方法
person.print();
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Person {
void print() {
System.out.print("person ----------------");
}
}
二、基本数据类型
java中的基本数据类型(boolean、byte、char、short、int、long、float、double)以及void关键字等都存在类类型。
public class TypeClass {
public static void main(String[] args) {
Class c1 = int.class;
Class c2 = Integer.class;
Class c3 = double.class;
Class c4 = Double.class;
Class c5 = void.class;
Class c6 = Void.class;
System.out.println("int.class = " + c1.getName());
System.out.println("Integer.class = " + c2.getName());
System.out.println("double.class = " + c3.getName());
System.out.println("Double.class = " + c4.getName());
System.out.println("void.class = " + c5.getName());
System.out.println("Void.class = " + c6.getName());
}
}
执行后的打印日志:
int.class = int
Integer.class = java.lang.Integer
double.class = double
Double.class = java.lang.Double
void.class = void
Void.class = java.lang.Void
getName( )
获取类类型的名称
getSimpleName( )
获取不带包名的类类型名称
Class c = Integer.class;
System.out.println("c.getName() = " + c.getName()); // c.getName() = java.lang.Integer
System.out.println("c.getSimpleName() = " + c.getSimpleName()); // c.getSimpleName() = Integer
三、动态加载类
Class.forName(“类的完整包名路径”)
不仅表示了类的类类型,还代表了动态加载类。
编译时刻加载类是静态加载类,运行时刻加载类是动态加载类。
静态加载示例:
public class Office {
public static void main(String[] args) {
start("Word");
}
/**
* new 创建对象是静态加载类,在编译时刻就需要加载所有可能使用到的类。
*/
private static void start(String arg) {
if ("Word".equals(arg)) {
Word w = new Word();
w.start();
}
if ("Excel".equals(arg)) {
Excel e = new Excel();
e.start();
}
}
}
上边示例代码其实只使用到了 Word 类,但是因为是静态加载,在编译时期就需要加载所有可能使用到的类。所以,当只提供了 Word 类的情况下,因为没有 Excel 类,在编译时期就会检测到 Excel 类不存在的异常,导致程序其实根本用不到 Excel 类的情况下,也会因为编译时期报错而导致异常。
动态加载示例:
public class OfficeBetter {
public static void main(String[] args) {
run("Word");
run("Excel");
}
/**
* 根据文件类型动态加载对应的类,并执行其中的 start() 方法
* @param name 代表文件的类型
*/
private static void run(String name) {
try {
Class<?> c1 = Class.forName("com.example.office." + name);
// 这里因为动态加载,无法确定加载的类是Word还是Excel,所以抽出一个接口类 OfficeAble
// 让 Word 和 Excel 都实现 OfficeAble 并实现 start() 方法即可
OfficeAble o = (OfficeAble) c1.newInstance();
o.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
这里即使我们只使用Word类,也不会出现静态加载所产生的问题。同时如果后期需要添加新的功能,比如加入一个 PPT 的类并要执行一定的逻辑,这时不需要改动 OfficeAble 中的代码逻辑,只需要创建出 PPT 类并实现 OfficeAble 接口,将相应的逻辑功能在 start( )方法中实现即可。这就是动态加载相对的一些优势。
四、获取方法信息
方法也是对象,是 java.lang.reflect.Method 的实例对象, Method 类封装了关于类方法的操作。
先通过上面三种获取类类型的任意一种方式获取到类类型,例如:
String s = "abc";
Class c = s.getClass();
Class.getMethods()
Method[] methods = c.getMethods(); // 【注意】获取的是所有public的方法,包括从父类继承而来的
Class.getDeclaredMethods()
Method[] methods = c.getDeclaredMethods(); // 【注意】获取的是所有该类自己声明的方法,不问访问权限(不包含父类继承来的方法)
Method常用方法
method.getReturnType().getSimpleName(); // getReturnType() 得到方法返回值类型的类类型
method.getName(); // getName() 得到方法的名称
Class<?>[] paramTypes = method.getParameterTypes(); // getParameterTypes() 得到方法参数列表类型的类类型
五、获取成员变量信息
成员变量也是对象,是 java.lang.reflect.Field 的实例对象, Field 类封装了关于成员变量的操作。
先通过上面三种获取类类型的任意一种方式获取到类类型,例如:
String s = "abc";
Class c = s.getClass();
Class.getFields()
Field[] fields = c.getFields(); // 【注意】获取的是所有public的成员变量信息,包括从父类继承而来的
Class.getDeclaredFields()
Field[] fields = c.getDeclaredFields(); // 【注意】获取的是所有自己声明的成员变量信息
Field常用方法
Field[] fields = c.getFields();
for (int i = 0; i < fields.length; i++) {
// 可以直接打印成员变量的信息
// System.out.println(fields[i].toString());
Class<?> type = fields[i].getType(); // getType() 获得成员变量类型的类类型
System.out.println(type.getSimpleName() + " " + fields[i].getName());
}
六、获取构造函数信息
构造函数也是对象,是 java.lang.reflect.Constructor 的实例对象, Constructor 类封装了关于构造函数的操作。
先通过上面三种获取类类型的任意一种方式获取到类类型,例如:
String s = "abc";
Class c = s.getClass();
Class.getConstructors()
Constructor[] constructors = c.getConstructors(); // 【注意】获取的是所有public的构造函数
Class.getDeclaredConstructors()
Constructor[] declaredConstructors = c.getDeclaredConstructors(); // 【注意】获取所有的构造函数(不问访问权限,构造函数都是自己声明的)
Constructor常用方法
Constructor[] declaredConstructors = c.getDeclaredConstructors();
for (int i = 0; i < constructors.length; i++) {
Constructor constructor = constructors[i];
String name = constructor.getName(); // getName() 获取构造函数名称
Class[] paramTypes = constructor.getParameterTypes(); // getParameterTypes() 获取构造函数参数列表类型的类类型
}
七、方法反射的基本操作
八、通过反射了解泛型
本人能力有限,如果此博文中有哪里讲得让人难以理解,欢迎留言交流,若有讲解错的地方欢迎指出,大家互相学期,共同进步!