文章目录
定义:根据现有对象倒推类的组成
最核心:在JVM中任何一个类都有一个唯一的Class对象 ,此对象记录该类的组成结构,通过该class对象,可以反向查找到这个类的信息,称之为反射;
class对象是在当类加载时由JVM产生,用户只能取得此对象,无法创建此对象; ?
1.获取一个类的对应的Class对象 (Class的C是大写)
要想在Java中应用反射,首先取得该类的Class对象;有三种方法可以获取:
(1)调用Object提供的getClass方法
class Person {
}
public class Test1 {
public static void main(String[] args) {
Person per = new Person();
System.out.println(per.getClass());
}
}
(2)类名称 . class
public class Test1 {
public static void main(String[] args) throws ClassNotFoundException{
Class cla = Test1.class;
System.out.println(cla);
}
}
(3)调用Class类提供的静态方法:Class . forName(类的全名称)
class Person {
}
public class Test1 {
public static void main(String[] args){
try {
Class<?> cla = Class.forName("com.yy.readBlackTree.Person");
System.out.println(cla.getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
输出:com.yy.readBlackTree.Person
2.当我们拿到一个类的class对象后,可以做什么呢?
1. 创建该类的新对象
反射与工厂模式
package www.bitten.java;
/**
* @Author : YangY
* @Description : 反射与工厂模式
* @Time : Created in 9:37 2019/3/17
*/
interface Buycomputer {
void buy();
}
class Lenove implements Buycomputer {
@Override
public void buy() {
System.out.println("买一台联想电脑");
}
}
class Mac implements Buycomputer {
@Override
public void buy() {
System.out.println("买一台苹果电脑");
}
}
class ComputerFactory {
public static Buycomputer getInstance(String computerClass) {
try {
Class<?> cls = Class.forName(computerClass);
Buycomputer computer = (Buycomputer) cls.newInstance();
return computer;
}catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (IllegalAccessException e){
e.printStackTrace();
}catch (InstantiationException e) {
e.printStackTrace();
}
return null;
}
}
public class Test1 {
public static void main(String[] args) {
ComputerFactory.getInstance("www.bitten.java.Lenove").buy();
}
}
2. 取得包名、父类、父接口信息
interface ILife {
}
interface IAnimal {
}
class Person implements ILife,IAnimal {
}
public class Test1 {
public static void main(String[] args) throws ClassNotFoundException{
Class<?> cla = Person.class;
//打印包名
System.out.println(cla.getPackage().getName());
//打印它的父类名称
System.out.println(cla.getSuperclass().getName());
//用这个类的Class对象生成它的父接口数组
Class<?>[] clasArr = cla.getInterfaces(); //也可写成Class[] ....
//输出父接口
for(Class<?> element: clasArr) {
System.out.println(element);
}
}
}
输出:
www.bitten.java
java.lang.Object
interface www.bitten.java.ILife
interface www.bitten.java.IAnimal
3. 取得构造方法、普通方法、普通属性(⭐)
Constructor :用来描述一个类的构造方法的类
(1)取得所有参数构造方法:
- Class 类提供的getConstructors() :只能取得权限为public的构造方法 ;(包访问权限都不行,只能public)
- Class类提供的getDeclaredConstructors():取得所有权限的构造方法 ;
class Person {
private String name;
private int age;
public Person() {
}
Person(String name) {
}
private Person(String name, int age) {
}
}
public class Test1 {
public static void main(String[] args) throws NoSuchMethodException {
Class<?> cla = Person.class;
//只能取得权限为public的构造方法
Constructor<?>[] constructor = cla.getConstructors();
//取得所有权限的构造方法
Constructor<?>[] constructors = cla.getDeclaredConstructors();
//输出权限为public的所有构造方法
for (Constructor<?> constructor1 : constructor) {
System.out.println(constructor1);
}
//输出所有权限的构造方法
System.out.println("--------------------------");
for (Constructor<?> constructor2 : constructors) {
System.out.println(constructor2);
}
}
}
输出:
public www.bitten.java.Person()
public www.bitten.java.Person()
www.bitten.java.Person(java.lang.String)
private www.bitten.java.Person(java.lang.String,int)
(2)取得指定参数的构造方法
- Class类提供的getConstructor(参数) : 只能取得权限为public的构造方法 (包访问都不行,只能public)
- Class类提供的getDeclaredConstructor(参数) : 可以取得类中所有构造方法,与权限无关
例码:
class Person {
private String name;
private int age;
public Person() {
}
public Person(String name) {
}
public Person(String name, int age) {
}
}
public class Test1 {
public static void main(String[] args)throws NoSuchMethodException{
Class<?> cla = Person.class;
//注意:getConstructor这个方法只能取得权限为public的构造方法
Constructor constructor = cla.getConstructor(String.class,int.class);
System.out.println(constructor.getName());
}
}
输出:
www.bitten.java.Person
(3)创建新对象
利用反射创建新对象有两种方法:
- 通过Class类的newInstance()方法 ,此方法只能调用无参构造来产生实例化对象;
- 通过Constructor的newInstance方法获取指定参数构造方法实例化的对象;
(1):通过Class类的newInstance()方法
class Person {
private String name;
private int age;
public Person() {
}
public Person(String name) {
}
public Person(String name, int age) {
}
public void fun() {
System.out.println("hello");
}
}
public class Test1 {
public static void main(String[] args)throws InstantiationException,IllegalAccessException{
Class<?> cla = Person.class;
//Class类的newInstance方法只能获取无参构造实例化的对象
Person per =(Person) cla.newInstance();
System.out.println(per);
per.fun();
}
}
输出:
www.bitten.java.Person@154617c
hello
结论:我们在编写Java程序时要养成保留无参构造的习惯 (⭐)
(2)通过Constructor的newInstance方法获取指定参数构造方法实例化的对象
class Person {
private String name;
private int age;
public Person() {
}
public Person(String name) {
this.name = name;
}
public Person(String name, int age) {
}
public void fun() {
System.out.println("hello");
}
public String getName() {
return name;
}
}
public class Test1 {
public static void main(String[] args)throws NoSuchMethodException,InstantiationException
,IllegalAccessException,InvocationTargetException {
Class<?> cla = Person.class;
Constructor constructor = cla.getConstructor(String.class);
Person per =(Person) constructor.newInstance("yy");
System.out.println(per);
System.out.println(per.getName());
}
}
(4)取得方法和调用方法(⭐)
Class类中提供了获取所有普通方法和获取指定参数普通方法的方法;
1.获取所有普通方法(返回值类型都为Method[ ])
- (1):getMethods(),该方法返回对应的所有普通方法(不能返回private修饰的方法) ,它还返回父类,包括Object类的相关方法,也能返回所实现接口的普通方法;
- (2):getDeclaredMethods(),该方法返回对应的所有普通方法,包括private修饰的普通方法,它不会返回父类的方法,但它能返回所实现接口中的方法;
最大区别:(1)方法能返回所继承父类的方法,而(2)不能;⭐
2.获取指定参数的普通方法
public Method getMethod(String name, Class<?>... parameterTypes)
class PP {
public void funfun() {
}
}
class Person extends PP{
private String name;
public Person() {
}
public String getName() {
return name;
}
public void fun() {
}
public void setName(String name) {
this.name = name;
}
}
public class Test1 {
public static void main(String[] args) throws Exception{
Class cla = Person.class;
Person per = (Person)cla.newInstance();
//通过class对象的getDeclaredMethods()方法获取所有普通方法;
Method[] arrMethod = cla.getDeclaredMethods();
for(Method method:arrMethod) {
System.out.println(method);
}
Method method1 = cla.getMethod("setName", String.class);
Method method2 = cla.getMethod("getName"); //该方法需要抛出NoSuchMethodException;
//要调用普通方法,必须得先创建它的实例化对象,per则是实例化
method1.invoke(per,"yy");
System.out.println(method2.invoke(per)); //输出yy
}
}
输出:
public void www.bitten.java.Person.fun()
public java.lang.String www.bitten.java.Person.getName()
public void www.bitten.java.Person.setName(java.lang.String)
yy
可见getDeclaredMethods方法没有返回父类的方法;
3. 获取私有方法以及调用私有方法(setAccessible⭐)
class Person {
String name;
private void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class Test1 {
public static void main(String[] args) throws Exception {
//获取Person类的Class对象
Class<?> cla = Class.forName("com.yy.readBlackTree.Person");
//获取该类的实例化对象,通过无参构造生成(上面的Person类默认有无参构造,但如果上面只有有参构造的话,则必须显示的加上无参构造)
Person person = (Person) cla.newInstance();
//获取方法对象,因为这个方法是private修饰的,所以必须用getDeclaredMethod而不是getMethod
Method method = cla.getDeclaredMethod("setName", String.class);
//通过设置这个可以调用private修饰的方法⭐,否则会报错
method.setAccessible(true);
//调用方法,参数需要该方法的一个实例化对象,还有它的所有参数;必须填完所有参数,否则抛出NoSuchMethodException;
method.invoke(person, "Bob");
System.out.println(person.getName()); //输出:Bob
}
}
特别注意:
- 要想调用一个private修饰的方法,必须调用这个方法对象的setAccessible方法,并且设置参数为true,不调用该方法的时候默认为false;
- 另外,获取私有方法对象需要用getDeclaredMethod方法;
(5)取得属性
前提:类中的所有属性一定在类对象实例化之后才会进行空间分配,所以此时如果要想调用类的属性,必须保证有实例化对象。通过反射的newInstance()可以直接取得实例化对象(Object类型)
在Class类中提供有两组取得属性的方法:
- 第一组(父类中)-取得类中全部属性: public Field[] getFields() throws SecurityException
- 第一组(父类中)-取得类中指定名称属性: public Field getField(String name) throws
NoSuchFieldException, SecurityException - 第二组(本类中)-取得类中全部属性: public Field[] getDeclaredFields() throws SecurityException
- 第二组( 本类中)-取得类中指定名称属性 : public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException
而后就需要关注属性的核心描述类:java.lang.reflect.Field,在这个类之中有两个重要方法:
- 设置属性内容 : public void set(Object obj, Object value) throws IllegalArgumentException,
IllegalAccessException- 取得属性内容 : public Object get(Object obj) throws IllegalArgumentException,
IllegalAccessException
1. 获取所有属性或者单个属性
class Person {
private String name;
public Integer age;
String adress;
}
public class Test1 {
public static void main(String[] args) throws Exception {
//获取Person类的Class对象
Class<?> cla = Class.forName("com.yy.readBlackTree.Person");
System.out.println("==================获取权限为public的所有属性====================");
Field[] arr = cla.getFields();
for(Field field: arr) {
System.out.println(field.getName());
}
System.out.println("==================获取所有属性====================");
Field[] arr2 = cla.getDeclaredFields();
for(Field field: arr2) {
System.out.println(field.getName());
}
System.out.println("==================获取指定属性=====================");
//因为name这个属性是private修饰的,所以不能用getField()方法;
Field field = cla.getDeclaredField("name");
System.out.println(field.getName());
}
}
输出:
获取权限为public的所有属性==
age
获取所有属性==
name
age
adress
获取指定属性===
name
2. 操作属性(Field类中的两个方法)
class Person {
private String name;
public Integer age;
String adress;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", adress='" + adress + '\'' +
'}';
}
}
public class Test1 {
public static void main(String[] args) throws Exception {
//获取Person类的Class对象
Class<?> cla = Class.forName("com.yy.readBlackTree.Person");
Person person = (Person) cla.newInstance();
Field field = cla.getDeclaredField("name");
//因为name属性是private修饰的,所以需要设置为true,除了private,其他的访问权限都不需要设置setAccessible方法为true;
field.setAccessible(true);
field.set(person, "Bob");
System.out.println(person);
//注意哟:get方法传入的参数是实例化对象而不是Class对象哟!!⭐
System.out.println(field.get(person));
}
}
输出:
Person{name=‘Bob’, age=null, adress=‘null’}
Bob
4. Java反射的继承结构
注意:
-
绿色箭头都是继承(extends) 关系;
-
AccessibleObject类提供了setAccessible()方法,这是一个很重要的方法,能够让反射变得更将大,即能够操作由private访问权限的构造器、方法、属性;
public void setAccessible(boolean flag) throws SecurityException
-
可以看到这些类都是在reflect包下,注意的是Class类与其余几个类都没有直接关系,只是Class类提供了获取那几个类的对象的方法(上面已经详细讲述过了);