Java反射机制介绍与Class类的基本能使用(工厂模式)

1.Java反射机制

1.1 反射机制是什么

在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法;并且对于任意一个对象,都能够调用它的任意一个方法;这种动态获取信息以及动态调用对象方法的功能为Java语言的反射机制。

1.2 反射的应用场合

程序在运行时可能接受到外部传入的对象,该对象的编译时类型为Object,但是程序有需要改对象的运行时类型的方法。
为了解决这些问题,程序需要在运行时发现对象和类的真实信息。
如果编译时根本无法预知该对象和类属于哪些类,比如这些类都是以字符串形式存放在配置文件中的时候,程序只能依靠运行时信息来发现该对象和类的真实信息,此时必须使用反射了。

1.3 反射的常用类

  1. Class类(模板类):java.lang.Class 反射的核心类,可以获取类的属性、方法等信息;
  2. Field类:java.lang.reflec包中的类,表示类的成员变量,可以用来获取和设置类之中的属性值;
  3. Method类:Java.lang.reflec包中的类,表示类的方法,它可以用来获取类中的方法信息或者执行方法;
  4. Constructor类:Java.lang.reflec包中的类,表示类的构造方法。

1.4 反射使用步骤

获取想要操作的类的Class对象,他是反射的核心,通过Class对象我们可以任意调用类的方法;
调用Class类中的方法,即就是反射的使用阶段;
使用反射API来操作这些信息。

  • 类的对象:基于某个类new出来的对象,也称为实例对象。
  • 类对象:类加载的产物,封装了一个类的所有信息
    ————类名、父类、接口、属性、方法、构造方法

2. Class类

位置:java.lang.Class

  • 类对象,类的实例代表一个运行 类 java应用程序的类和接口。
public final class Class<T>
    extends Object
    implements Serializable, GenericDeclaration, Type, AnnotatedElement

2.1 常用方法和示例

常用方法:

String getName()
返回由 类对象表示的实体(类,接口,数组类,原始类型或空白)的名称,作为 String 。

static Class<?> forName(String className)
获取类对象名

Package getPackage()
获取类对象的包名

Class<? super T> getSuperclass()
获取父类的类对象名

Class<?>[] getInterfaces()
获取接口的类对象名

Constructor<?>[] getConstructors()
获取构造方法

Class<?>[] getParameterTypes()
获取方法(构造/成员)的参数类型列表

Field[] getFields()
获取属性(自身+父类的所有public公开属性)

Field[] getDeclaredFields()
获取属性(自身所有的属性)

Method[] getMethods()
获取方法(自身+父类单继承叠加的所有public公开方法)

Method[] getDeclaredMethods()
获取方法(自身所有的方法)

T newInstance()
创建由此类对象表示的类的新实例(此类对象必须有无参构造)。

方法使用示例:

public class TestClassMethods {
      public static void main(String[] args) throws Exception {
            // 获取类对象
            Class<?> c = Class.forName("com.day.methods.Student");
            System.out.println(c.getName()); //  com.day.methods.Student
            
            // 获得指定类对象的包名
            Package pack = c.getPackage();
            System.out.println(pack.getName()); // com.day.methods
            
            // 获得父类的类对象
            Class<?> superClass = c.getSuperclass();
            System.out.println(superClass.getName()); //  com.day.methods.Person
            
            // 获得接口的类对象
            Class<?>[] interfaces = c.getInterfaces();
            for (Class<?> inter : interfaces) {
                  // java.io.Serializable java.lang.Runnable  java.lang.Comparable
                  System.out.print(inter.getName() + " ");
            }
            
            // 获取属性(自身+父类的public公开属性)
            Field[] fields = c.getFields();
            for (Field field : fields) {
                  System.out.print(field.getName() + " "); // name age  name money
            }
            // 获取属性(自身所有的属性)
            Field[] fields2 = c.getDeclaredFields();
            for (Field field : fields2) {
                  System.out.print(field.getName() + " "); // name age  score
            }
            System.out.println("\n---------------------");
            
            // 获取方法(自身+父类单继承叠加的所有public公开方法)
            Method[] methods = c.getMethods();
            for (Method method : methods) {
                  // run compareTo exam play sleep eat wait wait wait  equals toString hashCode getClass notify notifyAll
                  System.out.print(method.getName() + " ");
            }
            System.out.println("\n---------------------");
            
            // 获取方法(自身所有的方法)
            Method[] methods2 = c.getDeclaredMethods();
            for (Method method : methods2) {
                  // run:void compareTo:int play:void exam:void
                  System.out.println(method.getName() + ":" +  method.getReturnType());
            }
            System.out.println("*************************");
            
            // 获取构造方法
            Constructor<?>[] cs = c.getConstructors();
            for (Constructor<?> cc : cs) {
                  // com.day.methods.Student:java.lang.String int
                  // com.day.methods.Student:java.lang.String
                  // com.day.methods.Student: (无参构造)
                  System.out.print(cc.getName() + ":");
                  Class<?>[] param = cc.getParameterTypes();
                  for (Class<?> p : param) {
                        System.out.print(p.getName() + " ");
                  }
                  System.out.println();
            }
            System.out.println();
            
            // new一个实例对象(必须要有无参构造)
            Object o = c.newInstance();
            Student stu = (Student)o;
            System.out.println(stu); //  com.day.methods.Student@3d4eac69
      }
}
class Person {
      public String name;
      public double money;
      public Person() {}
      public void eat() {}
      public void sleep() {}
}
@SuppressWarnings({ "serial", "rawtypes" })
class Student extends Person implements Serializable, Runnable,  Comparable{
      public String name;
      public int age;
      double score;
      
      public Student() {super();}
      public Student(String name) {}
      public Student(String name, int age) {}
      
      public void exam() {}
      public void play() {}
      @Override
      public void run() {}
      @Override
      public int compareTo(Object o) { return 0; }
}

2.2 获取Class对象的 3 种方法

① 通过类的对象,获取Class对象
② 通过类名获取一个Class对象
通过Class的静态方法forName()获取类对象 【最具普适性】

public class TestGetClassObject {
      public static void main(String[] args) throws  ClassNotFoundException {
            // 1.通过类的对象,获取Class对象
            Person p = new Person(); // 类的对象
            Class<? extends Person> c = p.getClass();
            System.out.println(c.getName());
            
            // 2.通过类名获取一个Class对象
            Class<Person> c2 = Person.class;
            System.out.println(c2.getName());
            
            // 3.通过Class的静态方法获取类对象 【最具普适性】
            Class<?> c3 = Class.forName("com.day.reflect.Person");
            System.out.println(c3.getName());
      }
      // 更通用的获取类对象的方法
      public static Class<?> getClassObject(String className) {
            Class<?> c = null;
            try {
                  c = Class.forName(className);
            } catch (ClassNotFoundException e) {
                  e.printStackTrace();
            }
            return c;
      }
}
class Person {
}

2.3 反射创建对象的 2 种方法

① 反射通过类对象创建类的对象
方法传参返回对象【普适性】

public class TestNewInstance {
      public static void main(String[] args) throws Exception {
            // 手工new对象
            Teacher t = new Teacher();
            t.name = "JJ";
            System.out.println(t.name);
            
            // 1.反射通过类对象创建类的对象
            Class<?> c = Class.forName("com.day.methods.Teacher");
            Teacher t2 = (Teacher)c.newInstance();
            t2.name = "Jerry";
            System.out.println(t2.name);
            
            // 2.方法传参返回对象【普适性】
            Teacher t3 =  (Teacher)createObject("com.day.methods.Teacher");
            System.out.println(t3);
      }
      
      public static Object createObject(String className) {
            try {
                  Class<?> c = Class.forName(className);
                  return c.newInstance();
            } catch (Exception e) {
                  e.printStackTrace();
            }
            return null;
      }
}
class Teacher {
      String name;
      public Teacher() { }
}

3. 工厂设计模式(示例)

  • 开发中有一个非常重要的原则“开闭原则”,对拓展开放、对修改关闭;
  • 工厂模式主要负责对象创建的问题;
  • 通过反射进行工厂模式的设计,完成动态的对象创建
public class TestNewInstanceForFile {
      public static void main(String[] args) throws Exception {
            // 创建出入流对象
            FileReader fr = new FileReader("files\\application.txt");
            BufferedReader br = new BufferedReader(fr);
            // 读出文件中的类的全限定名
            String className = br.readLine();
            
            // 创建Object对象返回后进行强转为对应类型,进而使用
            Teacher t3 = (Teacher)createObject(className);
            System.out.println(t3);
            
            br.close();
      }
      
      /**
       * 工厂:创建对象工厂
       * @param className String类型的类的全限定名
       * @return Object Object类型的对象
       */
      public static Object createObject(String className) {
            try {
                  Class<?> c = Class.forName(className);
                  return c.newInstance();
            } catch (Exception e) {
                  e.printStackTrace();
            }
            return null;
      }
}
/*
* files\\application.txt 内容:
* com.methods.Teacher
*/
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容