深入 Java 核心

6

// 类名.class  通过获取类的静态成员变量class得到(任何类都有一个隐含的静态成员变量class)

Class<?> clazz = String.class;

// 对象.getClass

Class<?> clazz2 = new String().getClass();

// Class.forName(“全量限定名”)

Class<?> clazz3 = Class.forName(“java.lang.String”);

  • (注意:这三种方式都是利用反射获取的都是同一个Class对象,这也叫做String的类类型,也就是描述何为类,一个类都有哪些东西,所以可以通过类类型知道一个类的属性和方法,并可以调用一个类的属性和方法,这就是反射的基础。)

反射

反射是指在程序的运行期间动态的去操作某个Class对象里面的成员(包括类信息、属性信息、方法信息等元素)。它可以让Java这种静态语言具备一定的动态性。目前大部分的开源框架实现都是基于反射的机制实现。

JVM → 类加载 → class文件 → 创建 → Class对象 → 构建类的实例 → instance(实例);

重点在运行时动态的操作Class对象。

反射机制的利与弊

为何要用反射机制?直接new对象不ok了吗,这就涉及到了动态与静态的概念

  • 静态编译:在编译时确定类型,绑定对象,即通过。

  • 动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有利于降低类之间的藕合。

  • 优点:

  • 可以实现动态创建对象和编译。比如,一个软件,不可能第一个版本就把它设计的很完美,当这个程序编译成功,发布后,当发现某些功能需要更新时,我们不可能要用户把旧版的卸载,再重新安装新的版本。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。

一句话总结:运行期类型的判断,动态类加载,动态代理就使用了反射

  • 缺点:

1.对性能有影响。反射相当于一系列解释操作,通知JVM要做的事情。性能比直接的java代码执行相同的操作要慢很多。

2.由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用,反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。

反射机制的相关操作

创建实例

1

2

3

4

// 在反射操作之前的第一步,就是要先获取Class对象

Class<?> clazz = Class.forName(“org.demo.reflect.bean.People”);

// 根据Class对象创建一个实例

clazz.newInstance();

动态操作属性

  • 通过Class对象可以动态的获取和操作类中的属性,属性在JDK中有一个类来进行封装,就是Field,Field提供了一些常用的API方法让我们去访问和操作类中的属性

getField() // 获取所有公开的属性字段(包括继承父类的公有属性)

getDeclaredField() // 获取本类所有(包括公有和私有,但是不包括父类的)的属性字段(注意:如果要访问和操作私有属性,必须调用setAccessible方法,打开访问开关)

getFields() // 获取所有公有的属性(包括继承自父类的公有属性)

getDeclaredFields() // 获取本类所有的属性(包括共有和私有的,但是不包括父类的)

set() // 给属性赋值,需要传入两个参数,第一个参数是当前类的一个实例,第二个参数是具体要赋予的值

get() // 获取属性的值,需要传入一个当前类的实例作为参数

getName() // 获取属性的名称

getType() // 获取属性的类型

isAnnotationPresent() // 判断该属性上是否定义了指定的注解,需要传入一个注解的Class对象作为参数

getAnnotation() // 获取当前属性上的注解对象,需要传入一个注解的Class对象作为参数

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

public static void main(String[] args) throws Exception {

// 在反射操作之前的第一步,就是要先获取Class对象

Class<?> clazz = Class.forName(“org.demo.reflect.bean.People”);

// 根据Class对象创建一个实例

Object instance = clazz.newInstance();

// 获取指定的属性

Field f1 = clazz.getField(“userName”);

// 获取属性的值,get方法需要传入一个当前类的实例

Object value = f1.get(instance);

System.out.println(value);

// 通过反射给属性赋值

// 第一个参数是当前类的实例,第二个参数是要赋予的值

f1.set(instance, “godql”);

value = f1.get(instance);

System.out.println(value);

// 获取一个私有的属性

// 如果需要访问和操作私有的成员,必须打开访问开关

// 打开访问开关其实就是破坏封装

Field f2 = clazz.getDeclaredField(“age”);

// 强制打开访问权限

f2.setAccessible(true);

Object value2 = f2.get(instance);

System.out.println(value2);

f2.set(instance, 30);

value2 = f2.get(instance);

System.out.println(value2);

// 获取属性的名称

System.out.println(f1.getName());

System.out.println(f2.getName());

// 获取属性的类型

System.out.println(f1.getType());

System.out.println(f2.getType());

// 获取所有公有的属性(包括继承自父类的公有属性)

Field[] fs1 = clazz.getFields();

// 获取本类所有的属性(包括共有和私有的,但是不包括父类的)

Field[] fs2 = clazz.getDeclaredFields();

// 判断当前属性上是否定义了注解

System.out.println(f1.isAnnotationPresent(MyAnno.class));

ystem.out.println(f2.isAnnotationPresent(MyAnno.class));

// 获取属性上定义的注解

MyAnno anno = f1.getAnnotation(MyAnno.class);

// 获取注解上的属性值

System.out.println(anno.name());

}

动态操作方法

  • 对于Class中的方法,API也提供了相应的类来进行封装,就是Method

getMethod() // 获取指定的公共的方法(包括继承自父类公共的),需要传递两个参数,第一个参数是方法名称,第二个参数是一个可变参数,传递的是方法参数的类型

getMethods() // 获取所有的公共的方法(包括继承父类的公共方法)。

getDeclaredMethod() // 获取本类中指定的方法(包括私有和共有的,不包括父类的),需要传递两个参数,第一个参数是方法名称,第二个参数是一个可变参数,传递的是方法参数的类型。如果是私有方法,同样需要先打开访问开关(setAccessible(true))。

getDeclaredMethods() // 获取本地中所有的方法(包括私有和公共的,不包括父类)

getName() // 获取方法名称

getReturnType() // 获取方法的返回值类型

getParameterTypes() // 获取方法中所有的参数类型

getParameterCount()// 获取方法中参数的总个数

getParameters() // (JDK1.8新特性)获取方法中所有的参数信息,每一个参数信息都是一个Parameter类的对象。可以通过这个对象获取各个参数的类型以及名称(注意:如果要获取参数名,在编译的时候需要加上一个parameters参数,如:javac -parameters Xxx.java。或者是在开发环境中设置相应的编译选项)。

invoke() // 回调当前方法,需要传递两个参数,第一个是当前类的实例,第二个是一个可变参数,需要传入调用方法是所需的参数值。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

public static void main(String[] args) throws Exception {

Class<?> clazz = Class.forName(“org.demo.reflect.bean.People”);

Object instance = clazz.newInstance();

// 获取指定的Method

Method m1 = clazz.getMethod(“say”, String.class, int.class);

// 获取方法名

System.out.println(m1.getName());

// 获取方法的返回值类型

System.out.println(m1.getReturnType());

// 获取方法的所有参数类型

Class<?>[] paramsType = m1.getParameterTypes();

for (Class<?> c : paramsType) {

System.out.println©;

}

// 获取参数名称(JDK1.8开始支持)

Parameter[] params = m1.getParameters();

for (Parameter p : params) {

System.out.println(“参数类型:”+p.getType());

System.out.println(“参数名称:”+p.getName());

}

// 通过当前的方法,获取定义这个方法的类

Class<?> c = m1.getDeclaringClass();

System.out.println(c.getName());

// 方法回调,目的就是通过反射去调用一个方法

m1.invoke(instance, “godql”, 21);

}

动态操作构造方法

  • Constructor是在反射API中用于封装构造方法的一个类,因此通过这个类可以获取构造方法的一些信息,以及通过这个对象来实例化一个类的实例。

getConstructor() // 获取无参并且公共的构造方法

getDeclaredConstructor() // 获取一个构造方法可以是私有的也可以是公共的,需要传入一个可变参数,就是构造方法的参数类型(注意:如果是私有的,必须先打开访问开关)

newInstance() // 通过构造方法创建实例,也需要传入一个可变参数,传入的是具体的值

getConstructors() // 获取所有公共的构造方法,返回的是一个Constructor数组

getDeclaredConstructors() // 获取所有的构造方法(包括私有和共有的),同样返回的是一个数组

getParameters() // 获取所有的参数对象,和Method一样

getParameterTypes() // 获取所有的参数类型,同Method一样

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

public static void main(String[] args) throws Exception {

Class<?> clazz = People.class;

// 获取无参的构造方法

Constructor<?> c1 = clazz.getConstructor();

// 获取构造方法的名称

System.out.println(c1.getName());

// 获取一个私有并且带参数的构造方法

Constructor<?> c2 = clazz.getDeclaredConstructor(String.class);

// 可以通过构造方法实例化一个对象

//(注意:如果默认有一个无参并且是公共的构造方法,

// 那么可以直接使用class.newInstance()方法创建实例,

// 如果构造方法是私有的,或者是带参数的,就必须先获取

// Constructor对象,在通过这个对象来创建类实例)

// 1.适用于无参并且是公共的构造方法

/*

Object instance = clazz.newInstance();

System.out.println(instance);

*/

// 2.适用于带参数或是私有的构造方法

// 由于构造方法也可以私有化,所以必须先打开访问开关

c2.setAccessible(true);

Object instance = c2.newInstance(“godql”);

System.out.println(instance);

// 获取所有public修饰的构造方法

Constructor<?>[] cons = clazz.getConstructors();

// 获取所有构造方法(包括私有的)

Constructor<?>[] cons2 = clazz.getDeclaredConstructors();

}

Class中的一些API

  • Class对象本身提供了很多的API方法用于获取和操作Class对象。

getPackage() // 获取当前类所在的包,使用Package对象进行封装,可以从中获取包的信息,例如:包名

getSimpleName() // 获取当前类的简单类名(不包括包名)

getName() // 获取当前类的完整类名(包括包名)

getSuperclass() // 获取当前类的父类,返回的也是一个Class对象

getInterfaces() // 获取当前类所实现的所有接口,返回的是一个Class数组

isAnnotationPresent() // 判断当前类上是否定义了注解

getAnnotation() // 获取类上定义的注解

通过反射了解集合泛型的本质

  • Java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译到了运行期就无效了。
1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

public static void main(String[] args) {

List list = new ArrayList();

List<String> list1 = new ArrayList<>();

list.add(“godql”);

// list1.add(20); 错误的

Class c1 = list.getClass();

Class c2 = list1.getClass();

System.out.println(c1 == c2); // 结果:true,说明类类型完全相同

// 反射的操作都是编译之后的操作(运行时)

/*

  • 以上说明编译之后集合的泛型是泛型擦除的

  • Java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译就无效了。

  • 验证: 通过方法的反射来操作,绕过编译

*/

try {

// 通过动态操作方法的反射得到add方法

Method m = c2.getMethod(“add”, Object.class);

// 方法回调 给list1添加一个int型的,这是在运行时的操作,所以编译器编译时没有泛型检查,所以不会报错

// 绕过编译操作

m.invoke(list1, 20);

// 验证是否有添加进list集合里
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

分享

1、算法大厂——字节跳动面试题

2、2000页互联网Java面试题大全

3、高阶必备,算法学习

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**[外链图片转存中…(img-08Q6GkKg-1713746073645)]

[外链图片转存中…(img-Ipa1BO6R-1713746073646)]

[外链图片转存中…(img-NFOm1vYh-1713746073646)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

分享

1、算法大厂——字节跳动面试题

[外链图片转存中…(img-6nJkUpFv-1713746073646)]

2、2000页互联网Java面试题大全

[外链图片转存中…(img-iKuaonqF-1713746073646)]

3、高阶必备,算法学习

[外链图片转存中…(img-ETjllTgv-1713746073646)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值