目录
定义
java.lang.Class类型:
所有的Java类型(包括基本数据类型、引用数据类型、void)
被加载到内存后,或者是编译器自动编译生成的class字节码,最终都会用一个Class对象来表示。 即,所有的Java类型,在内存中都表示为一个Class对象。
如何获取Class对象
(1)类名.class
(1)类型名.class
优点:最简洁
缺点:要求编译期这个类型就要存在
@Test
public void test01(){
Class c1 = int.class;//基本数据类型
Class c2 = void.class;//特殊的空类型
Class c3 = String.class;//系统定义的类类型
Class c4 = TestClass.class;//自定义的类的类类型
Class c5 = Serializable.class;//接口类型
Class c6 = ElementType.class;//枚举类型
Class c7 = Override.class;//注解类型
Class c8 = int[].class;
// Class c9 = Student.class;//错误的,因为编译期间不存在
}
(2)对象.getClass()
这个方法在java.lang.Object类型中声明的,返回对象的运行时类型
适用于:你的先有对象,适用于引用数据类型
@Test
public void test02(){
Class c2 = String.class;
Class c1 = "".getClass();
System.out.println(c1 == c2);//true
}
(3)Class.forName(“类型全名称”)
类型全名称:包.类名
优势:这个类型可以在编译期间未知,这个类名称可以在代码中出现,也可以配置在配置文件中,或者键盘输入等方式来指定。
@Test
public void test03() throws ClassNotFoundException{
Class c2 = String.class;
Class c1 = "".getClass();
Class c3 = Class.forName("java.lang.String");
System.out.println(c1 == c2);//true
System.out.println(c1 == c3);//true
}
(4)类加载器对象.loadClass(“类型全名称”)
一般都是用在自定义类加载器对象去加载指定路径下的类
@Test
public void test05() throws ClassNotFoundException {
Class c = TestClass.class;
ClassLoader loader = c.getClassLoader();
Class c2 = loader.loadClass("main.Employee");
Class c3 = Employee.class;
System.out.println(c2 == c3);//true
}
反射
声明类 --> 创建对象
反射: 获取Class对象 --> 创建对象
Java是静态语言,如果没有反射,Java必须在编译期间确定所有类型。Student stu= new Student(); 为了使Java也支持动态——在运行期间,确定某个类型的功能,才引入了反射机制。(只是延迟了确定对象的类型)
反射的应用
运行时获取任意类型的详细信息
以扩展包下的CustommizedClass类为例演示:
public class TestClass {
private Class clazz;
@Before
public void test01() throws Exception {
//如果这个类名是在配置文件中,先获取类名
Properties pro = new Properties();
pro.load(TestClass.class.getClassLoader().getResourceAsStream("demo.txt"));
String className = pro.getProperty("className");//key就是demo.txt文件中=左边的属性名
//(1)获取这个类的Class对象
clazz = Class.forName(className);
System.out.println(clazz);//class javaextra.guochao.demo.CustomizedClass
}
@Test
public void test02() {
//(2)获取类的详细信息
// clazz代表class javaextra.guochao.demo.CustomizedClass这个类
//获取包名
Package pkg = clazz.getPackage();
System.out.println("包名:" + pkg.getName());//包名:javaextra.guochao.demo
//获取类名
System.out.println("类名:" + clazz.getName());//类名:javaextra.guochao.demo.CustomizedClass
//类的修饰符
int mod = clazz.getModifiers();
System.out.println("修饰符的值:" + mod);//修饰符的值:1
System.out.println("修饰符:" + Modifier.toString(mod));//修饰符:public
//获取父类
Class sc = clazz.getSuperclass();
System.out.println("父类的名称:" + sc.getName());//父类的名称:java.lang.Object
//获取接口,因为接口可能有多个,所以用数组表示
Class[] interfaces = clazz.getInterfaces();
System.out.println("父接口们:");
for (Class inter :
interfaces) {
System.out.println(inter.getName());//父接口们:
}
//每一个属性就是一个Field的对象
/*
* (1)Field[] getFields() 得到所有公共的属性
* (2)Field[] getDeclaredFields() 得到所有声明的属性
*/
Field[] fields = clazz.getDeclaredFields();
int count = 0;
for (Field field : fields) {
count++;
int fMod = field.getModifiers();
System.out.print(count + ":属性的修饰符:" + Modifier.toString(fMod));
System.out.print(count + ":属性的数据类型:" + field.getType().getName());
System.out.println(count + ":属性的名称:" + field.getName());
}
/*
* Constructor[] getConstructors():得到所有的公共的构造器
* Constructor[] getDeclaredConstructors()():得到所有的声明的构造器
*/
count = 0;
Constructor[] constructors = clazz.getDeclaredConstructors();
for (Constructor constructor : constructors) {
count++;
int cMod = constructor.getModifiers();
System.out.println(count + ":构造器的修饰符:" + Modifier.toString(cMod));
System.out.println(count + ":构造器的名称:" + constructor.getName());
Class[] parameterTypes = constructor.getParameterTypes();
System.out.println(count + ":构造器的形参列表:" + Arrays.toString(parameterTypes));
}
/* (1)Method[] getMethods(); 得到所有公共的方法
* (2)Method[] getDeclaredMethods(); 得到所有声明的方法
*/
count=0;
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method method : declaredMethods) {
count++;
int mMod = method.getModifiers();
System.out.println(count + ":方法的修饰符:" + Modifier.toString(mMod));
System.out.println(count +":方法的返回值类型:" + method.getReturnType());
System.out.println(count + ":方法的名称:" + method.getName());
System.out.print(count + ":抛出的异常类型们:");
Class<?>[] exceptionTypes = method.getExceptionTypes();
System.out.println(Arrays.toString(exceptionTypes));
Class[] parameterTypes = method.getParameterTypes();
System.out.println(count + ":方法的形参列表:" + Arrays.toString(parameterTypes));
}
}
}
运行期间创建任意引用数据类型的对象
方式一:使用Class对象直接new对象(通过无参构造创建)
步骤:
(1)获取某个类型的Class对象
(2)通过Class对象来创建这个Class所代表的类型的对象
@Test
public void test01() throws ClassNotFoundException, InstantiationException, IllegalAccessException{
//(1)获取某个类型的Class对象
Class clazz = Class.forName("javaextra.guochao.demo.CustomizedClass");
//(2)创建对象
//obj的编译时类型,Object类型
//obj的运行时类型是CustomizedClass类型
Object obj = clazz.newInstance();//这里的newInstance()没有参数,因为它是用无参构造创建实例
System.out.println(obj);//CustomizedClass [id=0, info=null, num=0]
}
缺点:如果这个类没有无参构造,则会报错: java.lang.InstantiationException: javaextra.guochao.demo.CustomizedClass
Caused by: java.lang.NoSuchMethodException: javaextra.guochao.demo.CustomizedClass.< init >()
建议:
(1)创建对象方便
(2)继承时方便
子类构造器默认调用父类的无参构造
(3)反射创建方便
方式二:通过Class对象先获取有参构造,然后再创建对象
步骤:
(1)获取某个类型的Class对象
(2)通过Class对象来获取Constructor对象
(2)通过Constructor对象来创建这个Class所代表的类型的对象
@Test
public void test02() throws Exception{
//(1)获取某个类型的Class对象
Class clazz = Class.forName("javaextra.guochao.demo.CustomizedClass");
/*
* (1)Constructor clazz.getConstructor(Class<?>... parameterTypes)某个(public)公共的构造器
* (2)Constructor clazz.getDeclaredConstructor(Class<?>... parameterTypes)某个声明的构造器(所有权限)
* 一个类中可能存在多个构造器,但是多个构造器重载的话,形参列表一定不一样,所以通过形参列表就可以唯一的定位到一个构造器
* 如果Class<?>... parameterTypes,一个都不传,即获取无参构造
*/
//(2)获取有参构造对象
Constructor c = clazz.getDeclaredConstructor(int.class,String.class,int.class);
//如果是private构造器,也可以创造对象
c.setAccessible(true);
//(3)通过Constructor对象来创建实例对象
Object obj = c.newInstance(1,"郭超",10);//这里的newInstance(实参列表),因为它用有参构造创建对象
System.out.println(obj);//CustomizedClass [id=1, info=郭超, num=10]
}
运行时设置或获取属性的值
@Test
public void test01() throws Exception {
//(1)获取某个类型的Class对象
Class clazz = Class.forName("javaextra.guochao.demo.CustomizedClass");
//(2)创建对象
//obj的编译时类型,Object类型
//obj的运行时类型是CustomizedClass类型
Object obj = clazz.newInstance();//通过无参构造创建CustomizedClass的一个对象obj
Object obj2 = clazz.newInstance();//创建另一个对象 obj2
System.out.println(obj);//CustomizedClass [id=0, info=null, num=0]
//(3)为属性赋值
//①获取id属性的Field对象
Field idField = clazz.getDeclaredField("id");
//设置id属性可访问,因为id为private
idField.setAccessible(true);
//②为id属性赋值
//在为属性赋值时,要说为哪个对象的属性赋值
idField.set(obj, 3);
System.out.println(obj);//CustomizedClass [id=3, info=null, num=0]
//(5)获取num属性的值
Field numfield= clazz.getDeclaredField("num");
numfield.setAccessible(true);
Object value= numfield.get(obj);//获取obj对象的num属性值
System.out.println(value);//0
}
运行时调用对象的任意方法
@Test
public void test09() throws Exception{
Class clazz =Class.forName("javaextra.guochao.demo.CustomizedClass");
//调用非静态方法:setinfo
//一个类中方法是可能重载,如何定位到某一个方法 : 方法名 + 形参列表
Method method=clazz.getMethod("setInfo",String.class);
Object obj = clazz.newInstance();//非静态方法需先创建对象调用
method.invoke(obj, "郭超");
System.out.println(obj);
//调用静态方法 :public static void test(int a)
Method testMethod = clazz.getDeclaredMethod("test", int.class);
testMethod.invoke(null, 10);//如果obj位置传入null,表示调用静态方法
}
在运行时读取某个类的泛型实参
步骤:
(1)获取Class对象
(2)获取泛型父类
Type type = clazz.getGenericSuperclass();
ParameterizedType type = (ParameterizedType) clazz.getGenericSuperclass();
(3)获取类型实参
Type:代表Java的所有类型
(1)Class:代表的是普通的类型,没有泛型信息的
(2)ParameterizedType:参数化类型 例如:Father<Integer, String>
(3)GenericArrayType:泛型数组类型 例如:T[]
(4)TypeVariable:类型变量 例如:T
(5)WildcardType:带?通配符的泛型的类型 例如:ArrayList<?>
public class TestClass {
@Test
public void test01() {
//获取Son类的泛型父类的类型实参
//(1)获取Class对象
Class clazz = Son.class;
//(2)获取泛型父类
//获取普通父类
//Class fu = clazz.getSuperclass();
//System.out.println(fu);
ParameterizedType type = (ParameterizedType) clazz.getGenericSuperclass();
//(3)获取类型实参
Type[] types = type.getActualTypeArguments();
for (Type t : types) {
System.out.println(t);
}
/*class java.lang.Integer
class java.lang.String*/
}
}
//泛型类型形参:<T,U>
class Father<T,U>{
}
//泛型类型实参:<Integer, String>
class Son extends Father<Integer, String>{
}