这是本菜鸡面试时被问到的一个问题,觉得挺有意思的,遂打算写一篇文章简单的谈下自己的看法。

在讨论这个问题之前,让我们先来简单复习一下Java反射的知识。

Java 反射

一、类型信息

在回顾Java反射之前,我们先来看一下RTTI(Run-Time Type Identification),运行时类型识别,它能在运行时就能够自动识别每个编译时已知的类型。

理解RTTI在Java中的工作原理,首先需要知道类型信息在运行时是如何表示的,这是由Class对象来完成的,它包含了与类有关的信息。Class对象就是用来创建所有“常规”对象的,Java使用Class对象来执行RTTI,即使你正在执行的是类似类型转换这样的操作。

二、反射

如果不知道某个对象的确切类型,RTTI可以告诉你,但是有一个前提:这个类型在编译时必须已知,这样才能使用RTTI来识别它。并利用这些信息做一些有用的事情。换句话说,在编译的时候,编译器必须知道所有要通过RTTI来处理的类。

而反射则不然,它并不需要编译时获取类的信息!

Class类与java.lang.reflect类库一起对反射进行了支持,该类库包含Field、Method和Constructor类,这些类的对象由JVM在启动时创建,用以表示未知类里对应的成员。这样的话就可以使用Contructor创建新的对象,用get()和set()方法获取和修改类中与Field对象关联的字段,用invoke()方法调用与Method对象关联的方法。另外,还可以调用getFields()、getMethods()和getConstructors()等许多便利的方法,以返回表示字段、方法、以及构造器对象的数组,这样,对象信息可以在运行时被完全确定下来,而在编译时不需要知道关于类的任何事情。

接下来,本文就Field简单的举个例,看看反射有多强大。

首先来简单的定义一个小白鼠类。。。

class Person{public Person() {}public String name = "小明";public int age = 15;private int height = 170;private double weight = 55.6;@Overridepublic String toString() {return "我叫"+name+",我今年"+age+"岁了,我的身高为"+height+"cm,体重为"+weight+"kg";}
}

之后我们开始使用反射来实现一些骚操作~

public class Test {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException {System.out.println("通过反射获取Person的class对象");Class clazz = Class.forName("com.util.xgb.Person");System.out.println("根据Person的class对象来创建一个Person对象");Object obj = clazz.getConstructor().newInstance();System.out.println("先来给大伙自我介绍一下。。。");System.out.println(obj);System.out.println("----------------------------------------------------");System.out.println("通过反射拿到person对象的所有成员变量!连私有的我也要知晓!");Field[] fields = clazz.getDeclaredFields();for(Field f : fields) {System.out.println(f);}System.out.println("----------------------------------------------------");System.out.println("现在见证反射的强大威力,穿透Person的private屏障!改变私有字段的值!");Field field1 = clazz.getDeclaredField("weight");Field field2 = clazz.getDeclaredField("height");System.out.println("暴力反射,解除私有限定,使得我们可以更改私有属性的值!");field1.setAccessible(true);field1.set(obj, 60.5);field2.setAccessible(true);field2.set(obj, 160);System.out.println("再来介绍一下自己吧。。。");System.out.println(obj);}

我们看一下打印输出结果。

通过反射获取Person的class对象
根据Person的class对象来创建一个Person对象
先来给大伙自我介绍一下。。。
我叫小明,我今年15岁了,我的身高为170cm,体重为55.6kg
----------------------------------------------------
通过反射拿到person对象的所有成员变量!连私有的我也要知晓!
public java.lang.String com.util.xgb.Person.name
public int com.util.xgb.Person.age
private int com.util.xgb.Person.height
private double com.util.xgb.Person.weight
----------------------------------------------------
现在见证反射的强大威力,穿透Person的private屏障!改变私有字段的值!
暴力反射,解除私有限定,使得我们可以更改私有属性的值!
再来介绍一下自己吧。。。
我叫小明,我今年15岁了,我的身高为160cm,体重为60.5kg

尽管举的例子很low,但是已经能够表达意思了,不管字段和方法被何种权限修饰符修饰,在反射面前,它们都是一丝不挂的。。。。

其实反射机制并没有什么神奇之处,当通过反射与一个未知类型的对象打交道时,JVM只是简单地检查这个对象,看它属于哪个特定的类。因此,那个类的.class对于JVM来说必须是可获取的,要么在本地机器上,要么从网络获取。所以对于RTTI和反射之间的真正区别只在于:

  • RTTI,编译器在编译时打开和检查.class文件
  • 反射,运行时打开和检查.class文件

那么Java反射破坏了封装性吗?

我们知道,封装、继承、多态是Java的三大特性!而我们通过上面简单的示例也看到了,通过反射,我们可以轻而易举的获取对私有成员的操作权利。那么这就是所谓的封装性遭受到破坏了吗?Java这样做岂不是搬起石头砸自己的脚吗?

我们不妨先来回想一下我们自己在使用Java撸代码的时候,声明私有方法时的出发点是什么?是不是大多数的情况下,这样做是为了支撑某一个我们对外提供的方法?并且,很多时候,我们为了代码的可读性会将一整个public方法拆分成很多个言简意赅、易读性更强的private方法和一个组合这些private方法的public方法。对于私有的成员变量,也是同理,使用private修饰变量,也是为了更好的服务于我们编写的某个对外提供的功能,而这些私有的成员变量和成员方法,仅在本类内有意义,如果对外开发的话,不仅毫无意义,甚至会严重影响到我们打算对外提供的功能。下面笔者拿ArrayList这个大家都很熟悉的类的源码举个例子:

    /*** Default initial capacity.*/private static final int DEFAULT_CAPACITY = 10;/*** The size of the ArrayList (the number of elements it contains).** @serial*/private int size;/*** Appends the specified element to the end of this list.** @param e element to be appended to this list* @return <tt>true</tt> (as specified by {@link Collection#add})*/public boolean add(E e) {ensureCapacityInternal(size + 1);  // Increments modCount!!elementData[size++] = e;return true;}private static int calculateCapacity(Object[] elementData, int minCapacity) {if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {return Math.max(DEFAULT_CAPACITY, minCapacity);}return minCapacity;}private void ensureCapacityInternal(int minCapacity) {ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));}private void ensureExplicitCapacity(int minCapacity) {modCount++;// overflow-conscious codeif (minCapacity - elementData.length > 0)grow(minCapacity);}/*** Increases the capacity to ensure that it can hold at least the* number of elements specified by the minimum capacity argument.** @param minCapacity the desired minimum capacity*/private void grow(int minCapacity) {// overflow-conscious codeint oldCapacity = elementData.length;int newCapacity = oldCapacity + (oldCapacity >> 1);if (newCapacity - minCapacity < 0)newCapacity = minCapacity;if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);// minCapacity is usually close to size, so this is a win:elementData = Arrays.copyOf(elementData, newCapacity);}

比如大家最熟悉的ArrayList为我们提供的add方法,我们可以看到它仅有短短的三行代码,但是其中有一个非常重要的操作就是需要确保底层的数组空间足够,如果不够需要执行相应的扩容。那么我们可以清晰的看到这些涉及检查容量/扩容的方法几乎都是private的,它对使用ArrayList的用户完全透明,但是却举足轻重,都是为了使public的add方法更加完整更加安全。

笔者挑选的这几个简单的私有成员来进行举例,它们无不是为了服务于ArrayList对外为我们提供的那些功能而存在,同时,如果这些私有成员对外开放,它们中的任何一个单独拿出来都将不具备任何意义,并且如果放宽这些私有成员的存取权限,很可能会导致对ArrayList的操作结果出乎我们的意料,甚至很有可能会出错。

现在,我们再回过头来想一想,Java反射破坏了封装性吗?
通过上述的分析我们现在应该有了答案

  • 一、用户通过反射机制获取了所使用的类中私有成员的存取权限,这是毫无意义的,因为我们上面分析过了,大多数的这些成员是依附于其它public方法而存在的,基本上都是为了服务这些public成员的。
  • 二、紧接着上面的来说,就算用户拿到了private成员的存取权限,而且还“恶意”的修改类的私有成员,那么这么做的目的何在呢?这将大概率导致类的功能无法正常提供,你这不是自己搞自己呢么。。。

写在最后:

  1. 上面第一部分关于反射部分的介绍,主要摘自《Thinking in Java》第四版的第十四章:类型信息部分。另外所介绍的内容不够详细,仅仅是为了引出下面对于文章题目的探讨,反射机制远比本文所介绍的更为强大更为丰富,不太熟悉的读者可以参考后面这个链接去学习一下,写的非常好。Java基础之—反射(非常重要) .
  2. 对于本文标题所指出的问题的看法(第二部分叙述的内容),都是本菜鸡自己的想法,可能会有些片面甚至很不成熟,如果哪里理解的有误,欢迎各位朋友指正,大家一起探讨学习,一起进步!

Java反射破坏了封装性?相关推荐

  1. 反射如何打破封装性_打破产品建议的复杂性

    反射如何打破封装性 当前系统的真正问题(The Real Issue With the Current Sytems) With the rise of e-commerce in this era, ...

  2. 第15天学习Java的笔记(封装性,this,构造方法)

    还有35天! 面向对象三大特征之封装性 package Demo1501;/** 面向对象三大特征:封装.继承.多态** 封装性在Java中的体现:* 1.方法就是一种封装* 2.关键字private ...

  3. Java的静态域以及封装性相关

    EmployeeTest类,用于定义一个简单的属性类. 1 package yang.src; 2 3 import java.util.Date; 4 5 public class Employee ...

  4. 让你觉得破坏了封装性的扩展方法

    扩展方法源于对扩展方法的了解是来自List<T>的Where.Order.GroupBy等方法的使用,智能感知提示这些方法都是扩展方法,于是MSDN上查阅后总结如下自定义扩展方法(将字符串 ...

  5. Java新职篇:多态性、封装性与继承性相互作用是什么?

    如果用得当,在由多态性.封装性和继承性共同组成的编程环境中可以写出比面向过程模型环境更健壮.扩展性更好的程序.精心设计的类层级结构是重用你花时间和努力改进并测试过的程序的基础,封装可以使你在不破坏依赖 ...

  6. 局部变量和成员变量;Java的封装性;private关键字和this关键字

    局部变量和成员变量的区别: 定义的位置不一样[重点] 局部变量在方法内: 成员变量在方法外,类以内: 作用范围不一样[重点] 成员变量在整个类都可以用 默认值不一样[重点] 成员变量在没有赋值的情况下 ...

  7. 反射 字段_详解面试中常考的 Java 反射机制

    反射(Reflection) 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说"自审",并能直接操作程序的内部属性和方法. 反射是一项高级 ...

  8. Java 反射 (Class、ClassLoader、Constructor、Method、Field)

    反射是Java中一个非常重要.非常强大的机制.曾看到一句话"反射是框架的灵魂",初学时不懂,等到学完框架之后才慢慢理解其意. 什么是反射?我们先通过几个类和示例来初步体会一下反射. ...

  9. java反射机制+继承设计技巧

    [0]README 0.1) 本文描述+源代码均 转自 core java volume 1, 旨在理解 java反射机制 :最后还顺带提出了 继承设计的技巧: [1]反射相关 1)反射定义:能够分析 ...

最新文章

  1. 远程连接云服务器的MySQL数据库
  2. 对计算机财务管理的理解,计算机财务管理
  3. PTA-7-1 输出大写英文字母 (15分)(C语言)
  4. ios 图片合成 处理合成模糊 水印 模板图片合成
  5. DirectX 下载地址
  6. swagger的详细注解
  7. linux openerp,Linux+OpenERP/ODOO 安装笔记求推荐。
  8. 量化分析(6)——K线图、交易量图、动量图、rsi强度图
  9. ebs查看服务状态_监控您的卷状态 - Amazon Elastic Compute Cloud
  10. 如何让计算机停止打印,电脑打印提示print Splooer停止无法使用怎么办
  11. 信息在计算机中用几进制表示,2017计算机等级考试知识点:数据在计算机中的表示...
  12. 大一 计算机应用基础 进制转换
  13. APP运营如何实现流量变现,获取更高收益?
  14. ESD静电保护二极管指南
  15. 基于UCOS-III的雷电传奇自制小游戏————秉火STM32F103-指南者;PS2手柄
  16. 框架分析--框架驱动
  17. python将英文翻译为中文_Python使用百度翻译开发平台实现英文翻译为中文功能示例...
  18. WPF 设置纯软件渲染
  19. Android 11.0 系统去掉多用户功能
  20. 【Alpha版本】冲刺阶段——Day 5

热门文章

  1. 提高sql查询效率速成宝典
  2. Oracle 视图(2)修改视图
  3. OpenWrt分区扩容
  4. 敏感词过滤器 filter
  5. 学习C#高级编程之XML
  6. 用java制作一个简易抽签器
  7. 计算机网络四种帧介绍,广播帧、未知帧、同网帧、异网帧
  8. 华星gps显示服务器错误,[科普] 差分GPS是怎么回事?
  9. Google 的左手 : 近距离观察 SEO
  10. tableau 如何选择tableau计算类型?基本计算 / LOD计算 / 表计算