Java 编程思想 Chapter_14 类型信息

本章内容绕不开一个名词:RTTI(Run-time Type Identification) 运行时期的类型识别

知乎上有人推断作者是从C++中引入这个概念的,反正也无所谓,理解并能串联本章知识才是最重要的

本章的内容其实都是为类型信息服务的,主要内容有

一.Class对象

问题:

1.Class对象的创建过程是怎么样的

2.Class对象有哪几种创建方式,之间有什么差异

3.使用泛型

在了解类型信息之前,需要了解class对象

创建class对象,需要先查找这个这个类的.class文件, 查找到的class文件会以字节码的形式加载到内存, 这时便可以通过内存中的Class对象 创建这个类的所有对象

本章中创建对象的方式有三种

第一种: 通过new 构造器 的方式

第二种: Class cls = Class.forName(“全限定名”); cls.newInstance();

第三种 Class cls = 类:.class; cls.newInstance();

说3种比较牵强,第二种和第三种通过虚拟构造器newInstance()的方式创建了对象, newInstance()有2点需要注意: ①接口不能newInstance;②类必须有的空构造器

这三种方式有什么差别呢,在这里会牵涉到其他的知识点

1.调用一个类的静态方法,有没有创建了一个对象

答案是没有的,因为没有涉及到以上3种创建对象的方式,只是将.class文件载入到了内存,对类进行了初始化, 并没有去创建对象

public class Test {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        A.print();
    }
}
class A{
    static{
        System.out.println("static 静态块");
    }

    public A(){
        System.out.println("构造方法");
    }

    public static void print(){
        System.out.println("打印class A");
    }
}

打印的结果如下:

static 静态块
打印class A

2. Class.forName(“全限定名”) 和 .class有没有差别

有差别, Class.forName会主动去在加载静态方法块, 而.class不会, .class对静态方法或非常数静态域首次引用后才进行初始化

public static void main(String[] args) {
        // TODO Auto-generated method stub
        try {
            Class.forName("chapter_14.A");
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

打印: static 静态块

public static void main(String[] args) {
        // TODO Auto-generated method stub
        Class cls = A.class;
}

没打印

提一点题外话, 为什么我们平时看到的被访问的静态常量通常是这样的

public static final int CONSTANT = 5;

而不是

public static int CONSTANT = 5;

原因在于static final 是编译期常量, 类.CONSTANT 不需要对类进行初始化就可以被读取

引入泛型的意义仅仅是为了提供编译期检查,第15章会着重讲讲泛型,在这里提供了几个概念

和平凡.class不一样, 泛型newInstance()返回该对象的确切类型

class A{}

class B extends A{}

Class<A> clsA = A.class;
A objA = clsA.newInstance();

那么超类呢?

Class<? super B> clsA = B.class.getSuperclass();
Object objA = clsA.newInstance();

超类得到不是精确类型,而只是Object

那继承类呢?

Class<? extends A> clsB = B.class;
A objA = clsB.newInstance();

此时得到的是父类的类型,在接下来的学习中,我们会经常用这种方式来创建对象,说白了这属于多态,属于类型信息

二.类型转换前先做检查

问题:

1.类型转换前先做的检查的意义和检查的方式

通过显式的向下转型,避免出现ClassCastException

常见的检查方式有3种,instanceof, isInstance, isAssignableFrom

举个例子:

class A implements Iface{}

class B extends A{}

interface Iface{}

第一种 instanceof

//类检查 本身类实例化对象的类型
Class<? super B> clsA = B.class.getSuperclass();
Object objA = clsA.newInstance();
System.out.println(objA instanceof A);//true

//类检查 子类实例化对象的类型
Class clsB = B.class;
Object objB = clsB.newInstance();
System.out.println(objB instanceof A);//true

//接口检查 实现类实例化对象的类型
System.out.println(objA instanceof Iface);//true
System.out.println(objB instanceof Iface);//true

第二种:isInstance

Class<? super B> clsA = B.class.getSuperclass();
Object objA = clsA.newInstance();

Class clsB = B.class;
Object objB = clsB.newInstance();
//类的Class对象检查 类本身的实例化对象
System.out.println(clsA.isInstance(objA));//true
System.out.println(clsB.isInstance(objB));//true
//类的Class对象检查 子类的实例化对象
System.out.println(clsA.isInstance(objB));//true

//接口的Class对象检查 实现类的实例化对象
Class clsIface = Iface.class;
System.out.println(clsIface.isInstance(objA));//true
System.out.println(clsIface.isInstance(objB));//true

第三种 isAssignableFrom

Class<? super B> clsA = B.class.getSuperclass();
Object objA = clsA.newInstance();

Class clsB = B.class;
Object objB = clsB.newInstance();
//类的Class对象检查 类本身的Class对象
System.out.println(clsA.isAssignableFrom(clsA));//true

//类的Class对象检查 子类的Class对象
System.out.println(clsA.isAssignableFrom(clsB));//true

//接口的Class对象检查 实现类的Class对象
Class clsIface = Iface.class;
System.out.println(clsIface.isAssignableFrom(clsA));//true
System.out.println(clsIface.isAssignableFrom(clsB));//true

对以上3种方式进行总结,便是以下这张图了

类型检查在另一方面也说明了 类与类的关系,类与接口的关系。

三.注册工厂

问题:

1.注册工厂有什么用

注册工厂是将工厂方法设计模式和添加融合在一起,在本章中,还是和类型信息有关系,在基类中添加实现类的对象,不过都是根据工厂设计模式去实现的,这样做的好处在于“避免新添加的数据对结构产生破坏”。本章中的例子很形象,也非常好,如果我有好的例子,也一定会放上链接

四.空对象

问题:

1.什么是空对象

2.使用空对象的意义

通常,空对象是一个单例,它具有无法修改的特性

假设一个类的 某个变量默认情况下是空对象,那么想要改变这个变量的属性,就需要重新创建一个对象来代替这个空对象,感觉我说的是废话,不过这是空对象的本质了,结合下以下代码,好好考虑下

interface Null{}

class Person{

    private final String first;
    private final String last;
    private final String address;
    public Person(String first, String last, String address) {
        super();
        this.first = first;
        this.last = last;
        this.address = address;
    }
    @Override
    public String toString() {
        return "Person [first=" + first + ", last=" + last + ", address=" + address + "]";
    }

    static class NullPerson extends Person implements Null{

        private NullPerson(){
            super("None", "None", "None");
        }

        @Override
        public String toString() {
            return "NullPerson";
        }

    }

    public static final Person NULL = new NullPerson();
}

这段代码抄自Java编程思想的空对象一节,另外有个知识点:不是每个类都会有默认的空构造器,像上面的Person类其实是没有空构造器的,问题在于构造器的参数用final修饰,可以去探究下。

五.反射

1.反射机制是怎么样的

2.如何通过动态代理的方式使用反射机制

反射是程序在运行时打开和检查.class文件,因此反射是动态的,JDK中使用Class类和java.lang.reflect类库对反射的概念进行了支持

使用反射是由于 某些类的属性,方法对外 没有包访问权限,而我们不得不进行访问,才能完成一些事情

可想而知,包访问权限对反射而言起不了作用,这里包括了private修饰, 私有内部类和匿名内部方法

应用到反射的例子有android中组件通信的EventBus,可以下载下来看源码

反射也可以用于动态代理(多说一句,动态代理本质还是类型信息)主要代码抄自书上

public class Test {

    public static void main(String[] args) {
        A objA = new A();

        Iface iface = (Iface) Proxy.newProxyInstance(Iface.class.getClassLoader(),
                new Class[]{Iface.class}, new DynamicProxyHandler(objA));
        doSomething(iface);
    }

    public static void doSomething(Iface iface){
        iface.doSomething();
    }
}

class A implements Iface{
    @Override
    public void doSomething() {
        System.out.println("A doSomething");
    }
}

interface Iface{

    public void doSomething();

}

//所有的调用都会重定向到这个单一的处理器上
class DynamicProxyHandler implements InvocationHandler{

    private Object proxied;

    public DynamicProxyHandler(Object proxy){
        proxied = proxy;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // TODO Auto-generated method stub
        return method.invoke(proxied, args);
    }
}

这里有3点需要注意的:1..重定向单一的处理器,调用的对象是什么; 2.创建的iface必须是接口对象,创建接口对象要传递的第二个参数是Class数组, 它包含了所有proxied的接口名称.class ; 3.动态代理与类型信息之间的关系

总结: 类型信息本质上还是关于 向上转型或者向下转型

时间: 2024-11-08 15:27:44

Java 编程思想 Chapter_14 类型信息的相关文章

编程思想14.类型信息

运行时类型信息使得你可以在运行时发现和使用类型信息,主要有两种方式: "传统的"RTTI,它假定我们在编译时已经知道了所有的类型: "反射"机制,它允许我们在运行时发现和使用类的信息. 14.1 为什么需要RTTI RTTI维护类型类型的信息,为多态机制的实现提供基础. 14.2 Class对象 类型信息在运行时是通过Class对象来表示的,完成的Class对象包含了与类有关的信息.Class对象就是用来创建所有"常规"对象的,Java使用Cla

java编程思想-枚举类型思维导图

后续加深理解,感觉java编程思想内容博大精深,java每个特性都有很全面深入的讲解,实际工作中一般很难会遇到,期望以后会用到 把书中的代码copy到eclipse中,理解会更快一些

12.JAVA编程思想——集合的类型

12.JAVA编程思想--集合的类型 欢迎转载,转载请标明出处:http://blog.csdn.net/notbaron/article/details/51100510 标准Java 1.0 和1.1 库配套提供了非常少的一系列集合类.但对于自己的大多数编程要求,它们基本上都能胜任.Java 1.2 提供的是一套重新设计过的大型集合库. 1      Vector Vector 的用法很简单,大多数时候只需用addElement()插入对象,用elementAt()一次提取一个对象,并用el

异常笔记--java编程思想

开一个新的系列,主要记一些琐碎的重要的知识点,把书读薄才是目的...特点: 代码少,概念多... 1. 基本概念 异常是在当前环境下无法获得必要的信息来解决这个问题,所以就需要从当前环境跳出,就是抛出异常.抛出异常后发生的几件事: 1.在堆上创建异常对象. 2.当前的执行路径中止                                          3. 当前环境抛出异常对象的引用.                                         4. 异常处理机制接

《Java编程思想(第4版)》pdf

下载地址:网盘下载 内容简介 编辑 本书赢得了全球程序员的广泛赞誉,即使是最晦涩的概念,在Bruce Eckel的文字亲和力和小而直接的编程示例面前也会化解于无形.从Java的基础语法到最高级特性(深入的面向对象概念.多线程.自动项目构建.单元测试和调试等),本书都能逐步指导你轻松掌握.[1] 从本书获得的各项大奖以及来自世界各地的读者评论中,不难看出这是一本经典之作.本书的作者拥有多年教学经验,对C.C++以及Java语言都有独到.深入的见解,以通俗易懂及小而直接的示例解释了一个个晦涩抽象的概

Java编程思想重点笔记(Java开发必看)

Java编程思想,Java学习必读经典,不管是初学者还是大牛都值得一读,这里总结书中的重点知识,这些知识不仅经常出现在各大知名公司的笔试面 试过程中,而且在大型项目开发中也是常用的知识,既有简单的概念理解题(比如is-a关系和has-a关系的区别),也有深入的涉及RTTI和JVM底层 反编译知识. 1. Java中的多态性理解(注意与C++区分) Java中除了static方法和final方法(private方法本质上属于final方法,因为不能被子类访问)之外,其它所有的方法都是动态绑定,这意

1.JAVA 编程思想——对象入门

对象入门 欢迎转载,转载请标明出处:    http://blog.csdn.net/notbaron/article/details/51040219 如果学JAVA,没有读透<JAVA 编程思想>这本书,实在不好意思和别人说自己学过JAVA.鉴于此,蛤蟆忙里偷闲,偷偷翻看这本传说中的牛书. 面向对象编程OOP具有多方面吸引力.实现了更快和更廉价的开发与维护过程.对分析与设计人员,建模处理变得更加简单,能生成清晰.已于维护的设计方案. 这些描述看上去非常吸引人的,不过蛤蟆还是没啥印象(至少到

JAVA编程思想学习笔记——第一章 对象导论

搞了一年多java,野路子出身,发现java基础这块还是相当的薄弱!故决定学习<Java编程思想>这本书.在此把学习的知识点记录下! 面向对象的五大特性 1.万物皆为对象 2.程序是对象的集合,它们通过发送消息来告诉彼此所要做的 3.每个对象都由自己的由其它对象所构成的存储 4.每个对象都拥有其类型 5.某一特定类型的所有对象都可以接收同样的信息  单根继承结构 所有的类都继承自单一的基类,Object.在单根集成结构中的所有对象都具有一个公用接口,所以他们归根到底都是相同的基本类型.单根集成

71.JAVA编程思想——JAVA与CGI

71.JAVA编程思想--JAVA与CGI Java 程序可向一个服务器发出一个CGI 请求,这与HTML 表单页没什么两样.而且和HTML 页一样,这个请求既可以设为GET(下载),亦可设为POST(上传).除此以外,Java 程序还可拦截CGI 程序的输出,所以不必依赖程序来格式化一个新页,也不必在出错的时候强迫用户从一个页回转到另一个页.事实上,程序的外观可以做得跟以前的版本别无二致. 代码也要简单一些,毕竟用CGI 也不是很难就能写出来(前提是真正地理解它).所以我们准备办个CGI 编程