Class类:
- Class也是类,因此也继承Object类
- Class类对象不是new出来的,而是系统创建的
- 对于某个类的Class类对象,在内存中只有一份,因此类只加载一次
- 每个类的实例都会记得自己是由哪个Class实例所产生
- 通过Class可以完整地得到一个类的完整结构,通过一系列API
- Class对象是存放在堆的
- 类的字节码二进制数据,是放在方法区的,有的地方成为类的元数据
(包括 方法代码,类变量,方法名,访问权限等等)
常用方法:
- static Class forName(String name) 返回指定类名name的Class对象
- Object newInstance() 调用缺省构造函数,返回该Class对象的一个实例
- getName() 返回此Class对象所表示的实体(类、接口、数组类、基本类型等)名称
- Class[] getInterfaces() 获取当前Class对象的接口
- ClassLoader getClassLoader() 返回该类的类加载器
- Class getSupreclass() 返回表示此Class所表示的实体的超类的Class
- Constructor[] getConstryctors() 返回一个包含某些Constructor对象的数组
- Field[] getDeclaredFields() 返回Field对象的一个数组
- Method getMethod(String name,Class … paramTypes) 返回一个Method对象,此对象的形参类型为paramType
class Car{//com.hspedu public String brand = "bmw"; public int price = 500000; public String color = "白色"; } public static void main(String[] args) { String classAllPath = "com.hspedu.Car"; //1、获取到Car类 对应的 Class对象 //<?>表示不确定的Java类型 Class<?> cls = Class.forName(classAllPath); //2、输出cls System.out.print(cls);//显示cls对象,是哪个类的Class对象 /* com.hspedu.Car */ System.out.print(cls.getClass());//输出cls运行类型 java.lang.Class //3、得到报名 System.out.print(cls.getPackage().getName());//包名 //4、得到全类名 System.out.print(cls.getName()); //5、通过cls创建对象实例 //Object o = cls.newInstance(); Car car = (Car)cls.newInstance(); System.out.print(car);//car.toString() /* Car{brand='bmw',price=500000,color='白色'} */ //6、通过反射获取属性brand Field brand = cls.getField("brand"); System.out.print(brand.get(car));//bmw //7、通过反射给属性赋值 brand.set(car,"奔驰"); System.out.print(brand.get(car));//奔驰 //8、遍历得到所有的属性 System.out.print("=======所有字段属性========"); Field[] fields = cls.getFields(); for (Field f : firlds) { System.out.print(f.getName());//名称 } }
获取Class对象:
- 前提:一直一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException,
实例:Class cls1 = Class.forName(“java.lang.Cat”)
应用场景:多用于配置文件,读取类全路径,加载类
String classAllPath = "com.hspedu.Car";//通过读取配置文件获取 Class<?> cls1 = Class.forName(classAllPath);
- 前提:若已知具体的类,通过类的class获取,该方式 最为安全可靠,程序性能最高,
实例:Class cls2 = Cat.class;
应用场景:多用于参数传递,比如通过反射得到对应构造器对象
Class cls2 = Car.class; System.out.print(cls2);
- 前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象,
实例:Class clazz = 对象.getClass();//运行类型
应用场景:通过创建好的对象,获取Class对象
Car car = new Car(); Class cls3 = car.getClass(); System.out.print(cls3);
- 其他方式:通过类加载器(有4种)来获取到类的Class对象
ClassLoader cl = 对象.getClass().getClassLoader(); Class clazz4 = cl.loadClass("类的全类名") //1、先得到类加载器car ClassLoader classLoader = car.getClass().getClassLoader(); //2、通过类加载器得到Class对象 Class cls4 = classLoader.loadClass(classAllPath);
cls1、cls2、cls3、cls4 都是同一个对象
- 基本数据(int,char,boolean,float,double,byte,long,short)按如下方式得到Class类对象
Class cls = 基本数据类型.class
Class<Integer> integerClass = int.class;
Class<Character> characterClass = char.class;
System.out.print(integerClass);//输出:int,自动装箱、拆箱
- 基本数据类型对应的包装类,可以通过.TYPE得到Class对象
Class cls = 包装类.TYPE
Class<Interger> type1 = Integer.TYPE;
Class<Character> type2 = Character.TYPE;
System.out.print(type1);//输出int
哪些类型有Class对象:
- 外部类、成员内部类、静态内部类、局部内部类、匿名内部类
- intreface:接口
- 数组
- enum:枚举
- annotation:注解
- 基本数据类型
- void
类加载:
基本说明:
反射机制是Java实现动态语言的关键,也就是通过反射实现类动态加载
- 静态加载:编译时加载相关的类,如果没有则报错,依赖性太强
- 动态加载:运行时加载需要的类,如果运行时不用该类,即使不存在该类,也不报错,降低了依赖性
加载时机:
- 当创建对象时(new) //静态加载
- 当子类被加载时,父类也加载 //静态加载
- 调用类种的静态成员时 //静态加载
- 通过反射 //动态加载
通过反射获取类的结构信息:
第一组:java.lang.Class类
- getName:获取全类名
- getSimpleName:获取简单类名
- getFields:获取所有public修饰的属性,包含本类以及父类的
- getDeclaredFields:获取本类中所有属性
- geyMethods:获取所有public修饰的方法,包含本类以及父类的
- getDeclaredMethods:获取本类种所有方法
- getConstructors:获取本类所有public修饰的构造器
- getDeclaredConstructors:获取本类中所有构造器
- getPackage:以Package形式返回 包信息
- getSuperClass:以class形式返回父类信息
- getInterfaces:以Class[]形式返回接口信息
- getAnnotations:以Annotation[]形式返回注解信息
第二组:java.lang.reflect.Field类
- getModifiers:以int形式返回修饰符
说明:默认修饰符是0,public 是1,private 是2,protected 是4,
static 是8,final 是16 //如:public(1) + static(8) = 9
-
getType:以Class形式返回类型
-
getName:返回属性名
第三组:java.lang.reflect.Method类
-
getModifiers:以int形式返回修饰符
说明:默认修饰符是0,public 是1,private 是2,protected 是4,
static 是8,final 是16
-
getReturnType:以Class形式获取 返回类型
-
getName:返回方法名
-
getParameterType:以Class[]返回参数类型数组
通过反射创建对象:
- 方式一:调用类中的public修饰的无参构造器
- 方式二:调用类中的指定构造器
- Class类相关方法:
- newInstance:调用类中的无参构造器,获取对应类的对象
- getConstructor(Class … clazz):根据参数列表,获取对应的所有构造器对象
- Constructor类相关方法
- setAccessible:爆破
- newInstance(Object…obj):调用构造器
例:
- 测试1:通过反射创建某个类的对象,要求该类中必须要有public的无参构造
-
测试2:通过调用某个特定构造器的方式,实现创建某类的对象
public static void main(String[] args) {
//1、先获取到User类的Class对象
Class> userClass = Class.forName(“com.hspedu.reflection.User”);
//2、通过public的无参构造器创建实例
Object o = userClass.newInstance();
System.out.print(o); //User [age=10,name=hsp]
//3、通过public的有参构造器创建实例
//3.1、先得到对应构造器
Constructor> constructor = userClass.getConstructor(String.class);
/*
constructor对象就是 public User(String name){……}
/
//3.2、创建实例,并传入实参
Object hsp = constructor.newInstance(“hsp”);
System.out.print(“hsp=” + hsp);
/
hsp=User [age=10,name=hsp]
/
//4、通过非public的有参构造器创建实例
//4.1得到private的构造器对象
Constructor<?> constructor1 = userClass.getConstructor(int.class,String.class);
//4.2创建实例
//爆破[暴力破解],使用反射可以访问private构造器/方法/属性
constructor1.setAccessible(true);
Object user2 = constructor1.newInstance(100,”张三丰”);
System.out.print(“user2=” + user2);
/
user2=User [age=100,name=张三丰]
*/
}class User{
private int age = 10;
private String name = “hsp”;
public User(){}
public User(String name){……}
private User(int age,String name){……}
public String toString(){……}
}
通过反射访问类中的成员:
访问属性:ReflecAccessProperty.java
1、根据属性名获取Field对象
Field f = clazz对象.getDeclaredField(属性名);
2、爆破:f.setAccessible(true); //f是Field
3、访问
f.set(o,值);
syso(f.get(o));
4、如果是静态属性,则set和get中的参数o,可以写成null
例:
public static void main(String[] args) {
//1、得到Student类对应的Class对象
Class<?> stuClass = Class.forName("com..hspedu.reflection.Student");
//2、创建对象
Object o = stuClass.newInstance();//o的运行类型就是Student
System.out.print(o.getClass());//Student
//3、使用反射得到age属性对象
Field age = stuClass.getField("age");
age.set(o,88)//通过反射来操作属性
System.out.print(o); //Student [age=88,name=null]
System.out.print(age.get(o));//返回age属性的值 88
//4、使用反射操作name属性
Field name = stuClass.getDeclaredField("name");
//对name进行爆破
name.setAccessible(true);
name.set(o,"hsp");
//或name.set(null,"hsp")
/*static属性是属于所有对象的,类加载的时候就有了*/
System.out.print(o);
/*Student [age=88,name=hsp]*/
System.out.print(name.get(o)); //获取属性值
//或 System.out.print(name.get(null)); //获取属性值,要求name是static
}
class Student{
public int age;
private static String name;
public Student(){}
public String toString(){……}
}
访问方法:
-
根据方法名和参数列表获取Method方法对象:
Method m = clazz.getDeclaredMethod(方法名,XX.class);
-
获取对象:Object o = clazz.newInstance();
-
爆破:m.setAccessible(true);
-
访问:Object returnValue = m.invoke(o,实参列表); //o就是对象
-
注意:如果是静态方法,则invoke的参数o,可以写成null
public static void main(String[] args) {
//1、得到Boss类对应的Class对象
Class<?> bossCls = Class.forName(“com.hspedu.reflection.Boss”);
//2、创建对象
Object o = bossCls.newInstance();
//3、调用public 的hi方法
//3.1、得到hi方法对象
Method hi = bossCls.getMethod(“hi”,String.class);
//或者
//Method hi1 = bossCls.getDeclaredMethod(“hi”,String.class);
hi.invoke(o,”hsoedu”);//4、调用private static对象 //4.1得到say方法对象 Method say = bossCls.getDeclaredMethod("say",int.class,String.class,char.class); //4.2爆破 say.setAccessible(true); say.invoke(o,100,"张三","男"); //4.3因为say方法是static的还可以这样调用 say.invoke(null,200,"李四","女"); //5、在反射中,如果方法有返回值,统一返回Object,但是他运行类型和方法定义的返回类型一致 Object returnVal = say.invoke(null,300,"王五","男"); System.out.print();
}
Class Boss{
public int age;
private static String name;public Boss(){} private static String say(int n,String s ,char c){//静态方法 return n + " " + s + " " + c; } public void hi(String s){ //普通public方法 System.out.print("hi" + s); }
}
单例模式与反射:
单例模式最根本的在于类只能有一个实例,如果通过反射来构建这个类的实例,单例模式就会被破坏
class GirlFrie{
private String name;
//2、在类的内部直接创建对象
//因为getInstance()是静态方法,静态方法只能访问静态属性
private static GirlFriend gf = new GirlFriend("小红红");
//1、将构造器私有化,防止直接new
private GirlFriend(String name){
this.name = name;
//3、提供一个公共的static方法,返回gf对象
private static GirlFriend getInstance(){
return gf;
}
}
public static void main(String[] args) {
//1、得到GirlFrie类对应的Class对象
Class<?> girlCls = Class.forName("com.GirlFrie");
//2、创建对象
Object o = girlCls.newInstance();
//4、调用private static对象
//4.1得到GirlFriend方法对象
Method GirlFriend = girlCls.getDeclaredMethod("GirlFriend",int.class,String.class,char.class);
//4.2爆破
say.setAccessible(true);
say.invoke(o,"张三");
}