这一章的知识在实际开发也没有那么重要,主要是了解即可,另外掌握如何使用反射机制。

类的使用:

在虚拟机中:

类的加载->类的连接->类的初始化

类的加载

  只会加载需要用到的类,加载到内存中,并创建对应的一个class文件,

  类加载到内存中,会创建一个class对象,

  class对象中保存了这个类中的方法、数据成员

  一个类加载一次

类加载器(JVM中)

  将.class文件加载到内存中,生成java.lang.Class类

  分为三种:

    Bootstrap ClassLoader 根类加载器

      核心类,比如java.lang.String

    Extension ClassLoader 拓展类加载器

      ext之类的

    System ClassLoader 系统类加载器

      自己写的类

反射

自己画了个思维导图,帮助大家理解。。

这个翻译不太好理解。

我们之前写的所有代码都没有涉及到反射

理解角度一:只有用到java.lang.class对象的时候,才叫使用反射

角度二:只有程序运行的时候,查看一个类有什么信息(数据成员、方法成员),这个过程叫反射。

角度三:在以往的开发中,我们创建一个类,我们知道类里面有啥方法,然后直接使用里面的方法、变量

      如果我们不知道我们要使用什么类,这个时候需要使用反射获取类的信息,再去使用里面的方法、变量

如果不能理解,也无所谓,在后面开发中再去体会。(不然就成书呆子了

  我一直认为,刚刚开始接触编程,最好抛开书本,尤其是教科书。

    教科书应该当作一本字典,哪里不会点哪里,千万不要一个字一个字看。

    教科书对于初学者来说没有太多的益处。

  个人认为最好的方法是:先写代码,再看理论;先学会使用,再了解原理。

怎么在实际开发中使用“反射”?

1-获取class对象

先获取一个类的class对象。注意我的描述,是某个类的class对象,类的class对象!

这里有个类:

public class User {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println(name);
}
}

第一种获取方式:

我们要获取这个User类的class对象,就使用getClass()方法,这个方法中Object中自带的,不用自己创建。

public class getClassTest {
public static void main(String[] args) {
User u1 = new User("remoo");
User u2 = new User("rcccc"); Class c1 = u1.getClass();
System.out.println(c1);
}
}

输出:

Class c1 = u1.getClass();
System.out.println(c1);
Class c2 = u2.getClass(); System.out.println(c1==c1);

输出:

说明c1和c2是一个类对象。

也就是说,每个类只会被加载class对象一次。

c1和c2的class对象都是user,无论创建了多少个User对象!

大家可以自己尝试获取上面代码中的getClassTest类的class对象,与c1的class做比较。

第二种获取方式:

直接通过类获取class,

直接某某类.class就可以了。

Class c3 = User.class;

第三种获取方式:

通过forName获取,

forName是Class中的静态方法。

try {
Class c4 = Class.forName("User");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}

这个方法要抛异常,原因是你填写的类名字编辑器不会帮你检查,有可能你写错了。

对了,如果你的类在包里,就要加上包的名字,

eg:com.test.User

更多例子:

错误的:

Class c4 = Class.forName("Math");

正确的:

Class c4 = Class.forName("java.lang.Math");

第三种方法用的比较多啦。

2-利用反射构造对象Constructor

我们获取这个类的方法:

public class User {
private String name;
private User(int a){System.out.println("private");}
public User() {
System.out.println("none");
}
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println(name);
}
}

获取方法:

1、获取全部构造方法

import java.lang.reflect.Constructor;
public class getConstructors {
public static void main(String[] args) throws Exception {
Class c = Class.forName("User");
Constructor[] cs = c.getConstructors();
for (Constructor constructor : cs)
System.out.println(constructor);
}
}

输出:

我们注意到,我们只能获取public的方法,私有的方法获取不到。

2、通过可变参数得到特定的构造方法

看到那三个...了吗,那个就是可变参数,参数数量可变。

Constructor constructor = c.getConstructor();

现在可以调用一波这个方法,

(但是实际开发中你肯定不知道这里面有啥方法,这里只是验证一下,这里得到的Object其实就是User)

Object o1 = constructor.newInstance();
User user =(User)o1;
user.show();

指定参数构造:

Constructor constructor = c.getConstructor(String.class);

对应User中的:

public User(String name) {
this.name = name;
}

验证:


如果想得到包括私有的所有构造方法,就使用:Declared

Constructor[] css = c.getDeclaredConstructors();

忽略访问权限!

但是在后面newInstance的时候不能直接调用,在newInstance之前调用一下:

constructor.setAccessible(true);

确保可以访问到里面的private对象。


3-获取字段getField

遍历所有的

import java.lang.reflect.Field;

class Test{
public int age;
public double height;
public String name;
public long money;
} public class getField {
public static void main(String[] args) throws Exception {
Class c = Class.forName("Test");
Field[] fs = c.getFields();
for (Field f : fs){
System.out.println(f);
}
}
}

返回:

查找特定名称的

Field f = c.getField("name");
System.out.println(f);

与上面反射构造方法后直接newInstance()不同,之前是创建一个对象

而现在我们要访问字段,我们需要明确是哪一个对象中的字段。

例子:

比如我们现在获取了一个Test对象t1,我们使用反射的方式获得其中的age字段。假设所有字段都已经赋了初值:

age 18 height 1.77 name "remoo" money 100

public class getField {
public static void main(String[] args) throws Exception {
Class c = Class.forName("Test");
Test t1 = new Test();
t1.age=18;
t1.height =1.77;
t1.name="remoo";
t1.money=100; Field ageField = c.getField("age");
System.out.println(ageField.getInt(t1));
}
}

c.getField("age")返回的是Test这个类的class对象中变量名为age的字段

再通过 字段.getInt() 或者 字段.getDouble() 或者 字段.get() 等方法,传入那个需要“反射”的对象,“反射”出字段。

  引用类型就用 get()

返回:

以前我们使用t1.age获得字段,

现在反射是ageField.t1

这就是反射。


访问匿名字段

和上面访问私有构造方法一样

我们把上面的例子中,Test类的age改成private的。

public class getField {
public static void main(String[] args) throws Exception {
Class c = Class.forName("Test");
Test t1 = new Test();
t1.height =1.77;
t1.name="remoo";
t1.money=100; Field age = c.getDeclaredField("age");
age.setAccessible(true); age.setInt(t1,18);
System.out.println(age.getInt(t1));
}
}

上述操作之后,也是ok的


4-获取调用成员方法

一个类里边有三大部分:

  1. 构造方法
  2. 成员变量(字段)
  3. 成员方法

但是成员方法相当于是1和2的结合

为什么这么说呢?

  和构造方法一样,成员方法需要传递可变参数

  和成员变量一样,成员方法需要传入一个对象

待反射的类:

class TestClass{
private int age;
public TestClass() {}
public TestClass(int age) {
this.age = age;
}
public void sayHello(String name){
System.out.println(name + age);
}
public void sayHello(){
System.out.println(age);
}
}

获取全部的方法(包括了系统内部的方法)

public class getMethod {
public static void main(String[] args) throws Exception {
TestClass tc = new TestClass(18);
Class c = Class.forName("TestClass"); Method[] ms = c.getMethods();
for (Method m : ms)
System.out.println(m);
}
}

调用方法invoke,带形参

Method m = c.getMethod("sayHello",String.class);
m.invoke(tc,"remoo");

返回:


调用私有化的方法

待测试类

class TestClass{
private int age;
public TestClass() {}
public TestClass(int age) {
this.age = age;
}
public void sayHello(String name){
System.out.println(name + age);
}
public void sayHello(){
System.out.println(age);
}
private void sayHello(int date){
System.out.println("private:"+date);
}
}

调用getDeclaredMethods()

我们注意到在使用getDeclaredMethods()的时候不会得到系统内部(父类)的方法。

调用带参数的私有化方法

Method m = c.getDeclaredMethod("sayHello",int.class);
m.setAccessible(true);
m.invoke(tc,19);

效果:


最新文章

  1. [python]python try异常处理机制
  2. LVS简介
  3. 求CRC校验和的低位和高位的两种方式
  4. html5 canvas图片翻转
  5. 【转载】#273 - Parameter Modifier Summary
  6. self和this的不同
  7. mysql 5.6
  8. Bootstrap框架菜鸟入门教程
  9. T-SQL动态查询(4)——动态SQL
  10. 20175120彭宇辰 《Java程序设计》第六周学习总结
  11. 模拟PLC 的圆弧插补方式在VC中绘制圆弧
  12. eclipse/idea远程调试Linux程序
  13. DNS的域名的解析解决办法(openDNS)
  14. hadoop脑裂
  15. java静态初始化数据
  16. 计算机科学基础知识(一)The Memory Hierarchy
  17. js里面return 和 return false的区别
  18. C#获取Url不同路径的方法大全
  19. web启动@Autowired不能自动注入
  20. Update语句到底是如何操作记录的?

热门文章

  1. Django 学习记录(AcWing)
  2. 【SpringBoot】YAML 配置文件
  3. Vue回炉重造之图片加载性能优化
  4. ansible变量引用
  5. ERROR: manifest for elasticsearch:latest not found: manifest unknown: manife
  6. MYSQL索引的建立、删除以及简单使用
  7. .NET(C#)发送邮件的实现方法
  8. 通过memberlist库实现gossip管理集群以及集群数据交互
  9. 5-2 Nacos注册中心
  10. 串口应用:遵循uart协议,发送多个字节的数据(状态机)