在说反射之前,我们先来思考一个具有争议的问题:
Java到底是不是纯面向对象语言?
那如何判断呢?
只要Java中的元素能够被类所描述、能够通过实例化获取到对象,就说这是面向对象的。
因为Java中的8大基本数据类型是不能实例化对象的,比如:int a = 8;
我们是不能通过a调用一个方法的,所以会有人说Java不是纯面向对象的语言。但其实Java就是一个纯面向对象的语言,因为基本类型它又有包装类型,可以调用它的包装类,这是一个类,所以它就是一个面向对象的语言,那既然有包装类,为什么还要有基本数据类型呢?这是为了体现Java的高性能,在使用的时候不用实例化对象。
首先什么是反射?
反射就是通过一个类对象获取到它的类。
例:
import java.util.Date;
public class Test{
public static void main(String[] args) {
Date date = new Date();//通过类实例化对象
Class ret = date.getClass();
System.out.println(ret.getName());
}
}
Class是一个泛型,那ret到底是哪一个Class呢?这就根据等号右边的对象是哪个类来判断了,如果它是Person类,那ret就是Class的Person类。
所有的类都抽象到了Class这个类中。
我们来看看下边这段代码,思考这两个Class对象是否相等。
如果相等,则一个Class类所表示的对象只有一个;如果不相等,则一个Class类所表示的对象有多个。
import java.util.Date;
public class Test{
public static void main(String[] args) {
Date date = new Date();//通过类实例化对象
Class ret = date.getClass();
System.out.println(ret.getName());
Date date2 = new Date();//通过类实例化对象
Class ret2 = date.getClass();
System.out.println(ret2.getName());
System.out.println(ret == ret2);
}
}
一个class对象,在同一个类加载器中,表示同一个类的时候,它的对象只能有且只有一个
有了反射,我们构造一个对象的时候,可以不再用new构造了,可以通过Class对象的方法获取实例化对象。
例:
import java.util.Date;
public class Test{
public static void main(String[] args) {
Date date = new Date();//通过类实例化对象
Class ret = date.getClass();
//通过Class对象的方法获取实例化对象
try {
//用Object类型的参数接收是因为,目前不知道是什么类型,Object是所有类的父类
Object object = ret.newInstance();
//判断object是不是Date类的对象
System.out.println(object instanceof java.util.Date);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
那如果它返回多个实例化对象,这些实例化对象是同一个吗?
我们可以验证一下:
import java.util.Date;
public class Test{
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
Date date = new Date();//通过类实例化对象
Class ret = date.getClass();
Class ret2 = date.getClass();
System.out.println(ret.newInstance() == ret2.newInstance());
}
}
会发现不是同一个实例化对象,说明每次实例化的时候都会实例化一个新的对象出来。
这就是反射,不再是通过一个类创建一个对象,而是通过调用对象方法创建一个对象;可以通过对象的getClass()方法获取到Class对象。
获取Class对象的方法可不止那一个,有如下:
- 通过对象调用getClass()获取
Class ret = date.getClass();
- 通过类的全限定名.class获取
Class ret = java.util.Date.class;
- 通过类的全限定名字符串获取
Class ret = Class.forName("java.util.Date");
那既然可以通过对象方法就实例化一个类对象,那我们就可以想起工厂设计模式的缺陷,每添加一个新的产品就需要修改工厂类,给它new一个新添加的类的对象,但如果我们使用反射,只需要在子类中实例化一个类对象,然后传入工厂类中,这样不管你新增多少个子类,工厂类都可以不用改变。
我们以前传统的工厂设计模式:
interface IFruit{
void eat();
}
class AppleImpl implements IFruit {
@Override
public void eat() {
System.out.println("吃苹果");
}
}
class FruitFactory{
public static AppleImpl getInstance(String className){
if("apple".equals(className)){
return new AppleImpl();
}
return null;
}
}
public class Test{
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
IFruit fruit = FruitFactory.getInstance("apple");
fruit.eat();
}
}
如果再加一个香蕉类,那它的工厂类也是要变化的:
interface IFruit{
void eat();
}
class AppleImpl implements IFruit {
@Override
public void eat() {
System.out.println("吃苹果");
}
}
class BananaImpl implements IFruit{
@Override
public void eat() {
System.out.println("吃香蕉");
}
}
class FruitFactory{
public static IFruit getInstance(String className){
if("apple".equals(className)){
return new AppleImpl();
}else if("banana".equals(className)){
return new BananaImpl();
}
return null;
}
}
public class Test{
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
IFruit fruit = FruitFactory.getInstance("banana");
fruit.eat();
}
}
这样是不是很麻烦呢?如果我们要加100类新产品,那工厂类就得new100次。
但有了反射,我们不管再加多少新种类,工厂类都可以不用变了。
interface IFruit{
void eat();
}
class AppleImpl implements IFruit {
@Override
public void eat() {
System.out.println("吃苹果");
}
}
class BananaImpl implements IFruit{
@Override
public void eat() {
System.out.println("吃香蕉");
}
}
class FruitFactory{
public static IFruit getInstance(String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
IFruit fruit = (IFruit)Class.forName(className).newInstance();
return fruit;
}
}
public class Test{
public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
IFruit fruit = FruitFactory.getInstance("www.bit.java.test.AppleImpl");
fruit.eat();
}
}
如果添加新类,只需要增加子类和改变传进入的类全名称即可。
如何取得类信息
取得包名称:
public class Test{
public static void main(String[] args) {
//获取类的包名
Class cl = Test.class;
Package pa = cl.getPackage();
System.out.println("Test类的包名是:"+pa.getName());
}
}
取得父类:
package www.bit.com;
class TestParent{
}
public class Test extends TestParent{
public static void main(String[] args) {
//获取类的包名
Class cl = Test.class;
Package pa = cl.getPackage();
System.out.println("Test类的包名是:"+pa.getName());
//获取类的父类
Class supperClass = cl.getSuperclass();
//获取父类的全名称
System.out.println("Test父类的全名称为:"+supperClass.getName());
//获取父类名称
System.out.println("Test父类的名称为"+supperClass.getSimpleName());
//获取类的实现接口
Class[] interfaceArray = cl.getInterfaces();
for(Class c : interfaceArray){
System.out.println("Tset类实现的接口有:"+c.getName());
}
}
}
取得构造
取得构造方法的好处是什么呢?
可以通过一个Constructor对象实例化一个对象不用new,是一个通用的方法,不管是有无参的还是有参的都能处理。
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
class TestParent{
}
interface interface1{
}
interface interface2{
}
public class Test extends TestParent implements interface1, interface2{
private int a;
public Test(){
}
public Test(int a){
this.a = a;
}
public Test(int a, String s){
}
public int getA(){
return a;
}
public static void main(String[] args) {
//获取类的包名
Class cl = Test.class;
Package pa = cl.getPackage();
/获取所有的构造方法
//通过构造方法实例化对象,解决class.newInstance()只调用无参构造的问题
Constructor[] constructors = cl.getConstructors();
for(Constructor c : constructors){
StringBuilder sb = new StringBuilder();
for(Class classe : c.getParameterTypes()){
sb.append(classe.getName()).append(",");
}
sb.setLength(sb.length() == 0 ? 0 : sb.length() - 1);
System.out.println(c.getName() + "(" + sb.toString() + ")");
}
//获取某一个构造方法
try {
Constructor constructor = cl.getConstructor(int.class);
Object obj = constructor.newInstance(6);
System.out.println(obj.getClass().getName());
Test test = (Test) obj;
System.out.println(test.getA());
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
通过类对象取得类方法
Method[] getMethods();
package www.bit.com;
import java.lang.reflect.Method;
class Person{
private Integer age;
private String name;
public Person(){
}
public Person(int age, String name){
this.age = age;
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
public class Test{
public static void main(String[] args) {
Class cl = Person.class;
//获取cl对象的方法
Method[] methods = cl.getMethods();
for(Method method : methods){
System.out.println(method.getName());
}
}
}
我们通过输出结果可以发现这些方法不仅仅只是Person类中的方法,还包括它父类的方法。
所以Method[] getMethods()这个方法获得的是取得全部的普通方法
Method[] getDeclaredMethods();//只获取自己的普通方法
package www.bit.com;
import java.lang.reflect.Method;
class Person{
private Integer age;
private String name;
public Person(){
}
public Person(int age, String name){
this.age = age;
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
public class Test{
public static void main(String[] args) {
Class cl = Person.class;
//获取cl对象本类的方法
Method[] methods = cl.getDeclaredMethods();
for(Method method : methods){
System.out.println(method.getName());
}
}
}
反射调用类中属性
Field[] getFields();----取得所有public权限的属性,包括父类
package www.bit.com;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
class Person{
public Integer age;
public String name;
public Person(){
}
public Person(int age, String name){
this.age = age;
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
class Student extends Person{
public String school;
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
}
public class Test{
public static void main(String[] args) {
Class cl = Student.class;
try {
//实例化一个Student类对象
Object studentObject = cl.newInstance();
//获取字段
Field[] files = cl.getFields();
for(Field field : files) {
System.out.println(field.getName()+" "+field.getType().getName());
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
Field[] getDeclaredFields();---返回本类的属性(访问权限不限)
package www.bit.com;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
class Person{
public Integer age;
public String name;
public Person(){
}
public Person(int age, String name){
this.age = age;
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
class Student extends Person{
public String school;
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
}
public class Test{
public static void main(String[] args) {
Class cl = Student.class;
try {
//实例化一个Student类对象
Object studentObject = cl.newInstance();
//获取字段,只获取自己的属性(public和private)
Field[] files = cl.getDeclaredFields();
for(Field field : files) {
System.out.println(field.getName()+" "+field.getType().getName());
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
我们还可以通过反射设置属性的值,即使这个属性被private封装也可以动态设置封装
动态设置封装:
public void setAccessible(boolean flag)---将flag换为true,即使该属性是私有的也可以通过反射修改
例:
package www.bit.com;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
class Person{
private String name;
public Person(){
}
public Person(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
public class Test{
public static void main(String[] args) {
Class cl = Person.class;
try {
//实例化一个Student类对象
Object studentObject = cl.newInstance();
//操作school属性
Field nameField = cl.getDeclaredField("name");
//取消封装
nameField.setAccessible(true);
//更换属性值
nameField.set(studentObject,"Calm");
System.out.println(studentObject);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
假如有Person per1 = new Person("Calm", 18) Person per2 = new Person();
我们现在要把per1的属性值统统赋给per2,如果Person类的属性只有这两个还好说,但若有上百个属性呢?这样手工赋值还不得累死了。
反射就在这里闪亮登场了,把反射用在这最能体现它的作用,用反射只需要把per1中的所有属性取出来,再把per2的属性取出来,在per1中找per2中凡是在per1中出现的就把值赋过去,这样一个循环就可以搞定了。
例:Person中有属性age、name、birthday,Student中有属性age、school、birthday,给Person中的所有属性手工赋值了,现在需要通过反射将Person中age、birthday赋给Student中得到age、birthday,因为Person中没有school属性,所以不能为school赋值。
package www.bit.com;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
class Person {
public int age;
public String name;
public Date birthday;
public Person(int age, String name, Date birthday) {
this.age = age;
this.name = name;
this.birthday = birthday;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
", birthday=" + birthday +
'}';
}
}
class Student{
public int age;
public String school;
public Date birthday;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", school='" + school + '\'' +
", birthday=" + birthday +
'}';
}
}
public class Test{
public static void main(String[] args) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("2001-07-03");
Person per = new Person(18,"Calm", sdf.parse("2001-07-03"));
Student stu = new Student();
Class perClass = per.getClass();
Class stuClass = stu.getClass();
//取得Person类中的所有属性
Field[] fields = perClass.getDeclaredFields();
//取得Student类中的所有属性
Field[] stuFields = stuClass.getDeclaredFields();
for(Field s : stuFields){
String stuFiledName = s.getName();
//依次取得Student中每个属性的set方法
String stuMethodName = "set"+stuFiledName.substring(0, 1).toUpperCase()
+(stuFiledName.length() > 1 ? stuFiledName.substring(1) : "");
//依次取得每个属性
for(Field f : fields){
try {
//依次取得Person类中每个属性的值
Object value = f.get(per);
//依次取得每个属性的名称
String filedName = f.getName();
//得到每个属性的set方法名 例:setName setAge setBirthday...
String methodName = "set"+filedName.substring(0, 1).toUpperCase()
//如果属性名是一个字母,则只取第一个字母的大写,后边取空字符串
+(filedName.length() > 1 ? filedName.substring(1) : "");
//保证Student中的属性在Person中也有,例:school在Person中没有,就不能进行赋值
if(stuMethodName.equals(methodName)){
//获得Student中某个属性的set方法
Method setMethod = stuClass.getDeclaredMethod(methodName, f.getType());
//将Person中该属性的值赋给Student中的该属性,例:相当于调用Student中的setName(value)
setMethod.invoke(stu, value);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
System.out.println(per);
System.out.println(stu);
}
}