Java编程思想(十五) —— 类型信息之反射
讲完.class,Class之后,继续。
1)泛化的Class引用
Class也能够增加泛型,增加之后会进行类型检查。
贴一下书上原话,Class<?>优于Class,尽管他们是等价的,Class<?>的优点是碰巧或疏忽使用了一个非详细的类引用。我搞不懂这个所谓非详细是什么?
后面弄懂了,事实上<?>作为通配符,就是未知的。直接写结论的话不能写个详细类型吧。作者的意思事实上就是说加了泛型的Class就是选择了非详细的版本号。
增加泛型的原因是提供编译期间的类型检查,操作失误的话便会显示错误。可是使用普通的Class的时候。就要到等到执行的时候。
还有这个Class<T>,JDK文档中:
T
- the type of the class modeled by thisClass
object. For example, the type ofString.class
isClass<String>
. UseClass<?
>
if the class being modeled is unknown.
<T>是使用泛型类的声明。包含:
Interface List<E>
- Type Parameters:
E
- the type of elements in this list
一样也是声明。
2)转型语法
SE5加入的用于Class引用转型的语法。
- class Gun{
- int price = 1;
- }
- class DeathGun extends Gun{
- int price = 2;
- }
- public class TestCast {
- public static void main(String[] args) {
- Gun g = new DeathGun();
- Class<DeathGun> d = DeathGun.class;
- //泛型的用处 类型不匹配
- //Class<DeathGun> dd = Gun.class;
- DeathGun gg = d.cast(g);
- System.out.println(gg.price);
- }
- }
- cast源代码:
- public T cast(Object obj) {
- if (obj != null && !isInstance(obj))
- throw new ClassCastException(cannotCastMsg(obj));
- return (T) obj;
- }
3)类型转换前的检查
- if(x instanceof TV){
- ((TV)x).show();
- }
检查对象类型。向下转型时假设有错,则抛出ClassCastException。所以要先使用instanceOf。
instaceOf与Class的等价性。
- class Father{}
- class Son extends Father{}
- public class Test {
- static void test(Object o ){
- System.out.println("type: "+o.getClass());
- System.out.println("o instanceof Father: "+(o instanceof Father));
- System.out.println("o instanceof Son: "+(o instanceof Son));
- System.out.println("Father.isInstanceOf(o)"+Father.class.isInstance(o));
- System.out.println("Son.isInstanceOf(o)"+Son.class.isInstance(o));
- System.out.println("o.getClass()==Father.class:"+(o.getClass()==Father.class));
- System.out.println("o.getClass()==Son.class:"+(o.getClass()==Son.class));
- System.out.println("o.getClass().equals(Father.class):"+(o.getClass().equals(Father.class)));
- System.out.println("o.getClass().equals(Son.class):"+(o.getClass().equals(Son.class)));
- }
- public static void main(String[] args) {
- test(new Father());
- test(new Son());
- }
- }
- result:
- type: class son.Father
- o instanceof Father: true
- o instanceof Son: false
- Father.isInstanceOf(o)true
- Son.isInstanceOf(o)false
- o.getClass()==Father.class:true
- o.getClass()==Son.class:false
- o.getClass().equals(Father.class):true
- o.getClass().equals(Son.class):false
- type: class son.Son
- o instanceof Father: true
- o instanceof Son: true
- Father.isInstanceOf(o)true
- Son.isInstanceOf(o)true
- o.getClass()==Father.class:false
- o.getClass()==Son.class:true
- o.getClass().equals(Father.class):false
- o.getClass().equals(Son.class):true
instanceof指的是你是这个类吗?你是这个类的派生类吗?
4)反射
书上讲得好简单,3页左右。
RTTI。执行时类型信息能够告诉你对象的详细类型,可是,这个类型必须在编译时必须已知,这样RTTI才干识别。
即在编译时,编译器必须知道要通过RTTI来处理的类。
这个看起来不是限制,可是置身于大规模的编程中,可能编译时程序无法获知这个对象所属的类。
RTTI和反射真正的差别仅仅有:RTTI,编译器在编译时检查和打开.class文件,对于反射,.class文件编译时不可获取。是在执行时打开和检查.class文件。
直接用书上的样例。贴个代码:
- public class ShowMethods {
- private static String usage =
- "usage:\n" +
- "ShowMethods qualified.class.name\n" +
- "To show all methods in class or:\n" +
- "ShowMethods qualified.class.name word\n" +
- "To search for methods involving 'word'";
- private static Pattern p = Pattern.compile("\\w+\\.");
- public static void main(String[] args) {
- if(args.length < 1) {
- System.out.println(usage);
- System.exit(0);
- }
- int lines = 0;
- try {
- Class<?> c = Class.forName(args[0]);
- Method[] methods = c.getMethods();
- Constructor[] ctors = c.getConstructors();
- if(args.length == 1) {
- for(Method method : methods)
- System.out.println(
- p.matcher(method.toString()).replaceAll(""));
- for(Constructor ctor : ctors)
- System.out.println(p.matcher(ctor.toString()).replaceAll(""));
- lines = methods.length + ctors.length;
- } else {
- //事实上作者这样写是。method匹配会匹配。假设我java ShowMethods ShowMethods ShowMethods
- //本来的话是提取当中的一个,可是args[1]为ShowMethods,刚刚好匹配到的就是ShowMethods本身自带的方法。也就是main方法。
- for(Method method : methods)
- if(method.toString().indexOf(args[1]) != -1) {
- System.out.println(
- p.matcher(method.toString()).replaceAll(""));
- lines++;
- }
- for(Constructor ctor : ctors)
- if(ctor.toString().indexOf(args[1]) != -1) {
- System.out.println(p.matcher(
- ctor.toString()).replaceAll(""));
- lines++;
- }
- }
- } catch(ClassNotFoundException e) {
- System.out.println("No such class: " + e);
- }
- }
- }
- F:\>java ShowMethods ShowMethods
- public static void main(String[])
- public final native Class getClass()
- public native int hashCode()
- public boolean equals(Object)
- public String toString()
- public final native void notify()
- public final native void notifyAll()
- public final native void wait(long) throws InterruptedException
- public final void wait(long,int) throws InterruptedException
- public final void wait() throws InterruptedException
- public ShowMethods()
慢慢看Matcher的replaceAll方法,不同于String的replaceAll,后者有两个參数。一个替换内容,一个是要被替换的内容。而前者仅仅有一个參数。
- public class TestString {
- public static void main(String[] args) {
- String s = "0898((*(*))(";
- //正則表達式编译进Pattern
- Pattern p = Pattern.compile("\\W");
- //p.matcher返回的是Matcher对象,Matcher的replaceAll方法是能够将匹配的字符串
- //替换成方法參数中的内容。
- System.out.println(p.matcher(s).replaceAll("-"));
- }
- }
- result:0898--------
假设没有正則表達式的替换,那么结果是:
- public static void ShowMethods.main(java.lang.String[])
- public final native java.lang.Class java.lang.Object.getClass()
- public native int java.lang.Object.hashCode()
- public boolean java.lang.Object.equals(java.lang.Object)
- public java.lang.String java.lang.Object.toString()
- public final native void java.lang.Object.notify()
- public final native void java.lang.Object.notifyAll()
- public final native void java.lang.Object.wait(long) throws java.lang.Interrupte
- dException
- public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedEx
- ception
- public final void java.lang.Object.wait() throws java.lang.InterruptedException
- public ShowMethods()
事实上就是替换掉类前面的包名。
有没有发现,forName的结果在编译时不可知,我们是在执行的时候传进Class參数的。尽管刚開始的时候没用到这么高级的东西。可是后面学得深入了之后就发现有些东西在使用,如Spring。
getMethods拿到的是类的全部共同拥有方法,包含自身,从父类继承。从接口实现的public方法。
另一个是getDeclaredMethods拿到的是类自身声明的方法。不止是public方法,protected也能拿到。甚至是private。(刚開始我也是非常疑惑,不是常常强调封装。私有不让人家看到吗。怎么能够直接拿到private的东西的,事实上能够这样看。什么东西有个度,可是这个度并非一尘不破。有一定的后门)。
getConstructors返回的是public的构造器。
试了一下,私有的构造器不会显示。书上说到这里就停了,继续扩展:
曾经看过Java反射教程里面介绍的非常好。
一、未知的方法调用
- public class TestRefl {
- public static void main(String[] args) {
- UnKnown uk = new UnKnown();
- Method m = null;
- try {
- m = uk.getClass().getMethod("print", new Class<?>[0]);
- m.invoke(uk);
- } catch (NoSuchMethodException | InvocationTargetException| SecurityException |IllegalAccessException e) {
- e.printStackTrace();
- }
- }
- }
- class UnKnown{
- public void print(){
- System.out.println("haha");
- }
- }
之前我特地将方法改成private,发现是找不到方法的,仅仅有改为public。
二、对象的创建
- public class TestRefl {
- public static void main(String[] args) {
- Class c = null;
- try {
- c = c.forName("son.UnKnown");
- System.out.println(c.getName());
- } catch (ClassNotFoundException e) {
- System.out.println("not found");
- }
- try {
- UnKnown u = (UnKnown) c.newInstance();
- u.print();
- } catch (InstantiationException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- }
- }
- class UnKnown{
- static void print(){
- System.out.println("haha");
- }
- }
通过构造函数获取对象。
- public class TestRefl {
- public static void main(String[] args) {
- Class c = null;
- try {
- c = c.forName("son.UnKnown");
- } catch (ClassNotFoundException e) {
- System.out.println("not found");
- }
- Constructor<?> ct[] = c.getConstructors();
- try {
- UnKnown u = (UnKnown) ct[0].newInstance();
- UnKnown u2 = (UnKnown) ct[1].newInstance(1);
- u.print();
- u2.print();
- } catch (InstantiationException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IllegalArgumentException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- class UnKnown {
- public UnKnown() {
- }
- public UnKnown(int i) {
- }
- static void print() {
- System.out.println("haha");
- }
- }
构造函数数组有顺序,0是无參的那个构造函数,1为传參,在方法中直接传參就可以。
当然私有的构造方法是拿不到的。
解释到RTTI和反射的不同的时候,反过来能够说明反射的作用。就是执行时检查对象的类型。随意调用对象的方法(Spring和servlet中都用到了,注意到没有,我们在xml配置,然后属性会依据xml注入),同一时候能够知道方法參数和属性。
反射还是非常强大。接下来会介绍动态代理,曾经被虐过的一个设计模式。
Java编程思想(十五) —— 类型信息之反射的更多相关文章
- Java编程思想学习笔记——类型信息
前言 运行时类型信息(RTTI:Runtime Type Information)使得我们可以在程序运行时发现和使用类型信息. Java在运行时识别对象和类的信息的方式: (1)一种是RTTI,它假定 ...
- 关于 java编程思想第五版 《On Java 8》
On Java 8中文版 英雄召集令 这是该项目的GITHUB地址:https://github.com/LingCoder/OnJava8 广招天下英雄,为开源奉献!让我们一起来完成这本书的翻译吧! ...
- Java编程思想学习(五)----第5章:初始化与清理
随着计算机革命的发展,“不安全”的编程方式已逐渐成为编程代价高昂的主因之一. C++引入了构造嚣(constructor)的概念,这是一个在创建对象时被自动调用的特殊方法.Java中也采用了构造器,并 ...
- java编程思想第五章初始化与清理
5.1使用构造器确保初始化: 构造器与一般方法一样,但是没有返回值,且其方法名与类名完全相同. 不接受任何参数的构造器成为默认构造器,也叫无参构造器. 5.2 方法重载: 为什么会有方法重载? 构造器 ...
- Java编程思想学习(五) 复用类
1.继承与组合 复用类的方法有两种:继承与组合.继承就不多说了,组合就是直接在类中new一个对象. 数组也是对象,使用数组也是组合的一种. 2.初始化基类 当创建一个导出类的对象时,该对象包含一个基类 ...
- java编程思想-第五章-某些练习题
参考https://blog.csdn.net/caroline_wendy/article/details/46844651 10&11 finalize()被调用的条件 Java1.6以下 ...
- 《Java编程思想》笔记 第十四章 类型信息
1.RTTI:在运行时识别一个对象类型 JAVA在运行时 有时要 识别对象和类的信息这个机制叫RTTI.Java提供了两种机制去做这件事.传统的RTTI 和 反射. 传统的RTTI 假定编译时就已经 ...
- Java 编程思想 Chapter_14 类型信息
本章内容绕不开一个名词:RTTI(Run-time Type Identification) 运行时期的类型识别 知乎上有人推断作者是从C++中引入这个概念的,反正也无所谓,理解并能串联本章知识才是最 ...
- 《java编程思想》读书笔记(一)开篇&第五章(1)
2017 ---新篇章 今天终于找到阅读<java编程思想>这本书方法了,表示打开了一个新世界. 第一章:对象导论 内容不多但也有20页,主要是对整本书的一个概括.因为已经有过完整JAV ...
随机推荐
- java classpath作用
环境变量的配置: 1):永久配置方式:JAVA_HOME=%安装路径%\Java\jdk path=%JAVA_HOME%\bin 2):临时配置方式:set path=%path%;C:\Progr ...
- C++“窗体”程序设计启蒙(之二)
[摘要]本文适合已经完整学习了C++面向对象机制,但在开发窗体程序方面还是零基础的同学.通过本文的引导进行实践体验,目的是消除同学们开发窗体程序的神奇感,为下一步的自学找到感觉.同一时候,能更深入地体 ...
- [转]C++之多态性与虚函数
面向对象程序设计中的多态性是指向不同的对象发送同一个消息,不同对象对应同一消息产生不同行为.在程序中消息就是调用函数,不同的行为就是指不同的实现方法,即执行不同的函数体.也可以这样说就是实现了“一个接 ...
- [leetcode]Construct Binary Tree from Preorder and Inorder Traversal @ Python
原题地址:http://oj.leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/ 题意:根 ...
- C# 开发者代码审查清单
这是为C#开发者准备的通用性代码审查清单,可以当做开发过程中的参考.这是为了确保在编码过程中,大部分通用编码指导原则都能注意到.对于新手和缺乏经验(0到3年工作经验)的开发者,参考这份清单编码会很帮助 ...
- 浅谈Jquery中的bind()、live()、delegate()、on()绑定事件方式
一.on(),live(),bind() on() 为指定的元素,添加一个或多个事件处理程序,并规定当这些事件发生时运行的函数.使用 on() 方法的事件处理程序适用于当前或未来的元素(比如由脚本创建 ...
- python - 增强的格式化字符串format函数
语法 它通过{}和:来代替%. “映射”示例 通过位置 In [1]: '{0},{1}'.format('kzc',18) Out[1]: 'kzc,18' In [2]: '{},{}'.form ...
- 【理解】column must appear in the GROUP BY clause or be used in an aggregate function
column "ms.xxx_time" must appear in the GROUP BY clause or be used in an aggregate functio ...
- SpringApplicationConfiguration 这个不能用 解决方案
使用的test包的版本号要与spring的一致,避免jar包依赖冲突 直接用注解 @RunWith(SpringRunner.class)@SpringBootTest @SpringApplicat ...
- [Javascript]Clouse Cove, 2 ,Modifying Bound Values After Closure
function buildCoveTicketMarker(transport){ var passengerNumber = 0; return function(name){ passenger ...