第十四章 类型信息

前言

在开始介绍类型信息之前一定要区分一些概念:

1、静态类型语言与动态类型语言

静态类型语言:在编译期进行类型检查的语言(例如,Java)。(例如 int i = 3;)

动态类型语言:在运行期进行类型检查的语言(例如,JavaScript)。最明显的特征为变量没有类型值有类型(如,var=3;)

2、类型检查与类型转换(在Java中的区别)

类型检查:发生在编译期,检查方法是不是接收到了合适类型的参数,赋值是不是有合适类型的右值。

类型转换正确性检查:发生在运行期,检查一个类型变换到另外一个类型是不是正确合法的。举个栗子:

class A {}
class B {}
public class Test {

    public static void main(String[] args) {

        Object o = new A();
        B b = (B)o;
    }

}

像这种明显偷梁换柱的不安全转换,但是,编译器却发现不了。因为编译器不知道对象o的实际类型,只知道o的编译时静态类型Object,只有当运行期A.class文件载入虚拟机,虚拟机进行类型转换正确性检查才会知道o的实际类型是A,将A赋值给B类型不正确,抛出运行时异常。

3、清楚Java的语言性质

Java是静态类型的语言,因为其在编译期接受静态的类型检查。Java是动态类型安全(动态 类型安全 这样断句)的语言,因为其在运行期进行类型转换正确性检查。

 4、RTTI与反射的区别

RTTI:运行时类型识别,可以看出它只是用来查询Class对象的一些简单信息。

反射:功能强大,可以绕过编译器调用方法。

最本质的区别是RTTI在编译时打开和检查class文件,反射是在运行时打开和检查class文件。这和虚拟机加载class文件没有冲突。

一、代表类型信息的Class对象

简述:每一个类经过编译都会生成一个ClassName.class文件,这个.Class文件就是表示类信息的二进制字节码文件。运行时,虚拟机的类加载系统会加载这个class文件,并生成Class对象,将class文件中的常量池放入方法区的运行时常量池,Class对象就代表着类型信息,并提供访问运行时常量池的途径。

1、示例一:类的加载行为

class Candy {
    static int i = 11;
    static{
        System.out.println("Loading Candy!");
    }
}

class Cookie {

    static {
        System.out.println("Loading Cookie!");
    }
}

class Gum {
    static final int i = 15;
    static {
        System.out.println("Loading Gum!");
    }
}
public class Demo1 {

    public static void main(String[] args) {

        new Cookie();
        System.out.println("Candy.i = " + Candy.i);
        System.out.println("Cookie.i = " + Gum.i);
    }

}

输出结果:

Loading Cookie!
Loading Candy!
Candy.i = 11
Cookie.i = 15

结果分析:只有两种行为可以引起类的加载,一种创建此类及其子类的对象,另一种是引用此类的静态成员。有一种情况特殊,编译期常量不会引起类的加载行为(这样说不准确,只不过看起来像),例如 static final int i = 3;。类加载过程分为:加载,验证、准备、初始化。编译期常量在类加载过程的准备环节就已经有值可用,其他类变量要到类加载的初始化环节才会被赋予值,所以static{}不会执行。

2、示例二:Class类的基本接口

interface Pet {}

interface Color {}

class Dog implements Pet, Color {}

class Mutt extends Dog {}

public class Demo2 {

    static void printInfo(Class<?> c) {
        System.out.println("Class name: " + c.getName() +
                " is interface? [" + c.isInterface() + "]");
        System.out.println("Canonical name: " + c.getCanonicalName());
        System.out.println("Simple name: " + c.getSimpleName());
        System.out.println("------------------------------------------");
    }
    public static void main(String[] args) {
        Class<?> c = null;
        try {
             c = Class.forName("rtti.Mutt");
        }catch(ClassNotFoundException e) {
            e.printStackTrace();
            System.out.println("Can‘t find Mutt");
            System.exit(1);//非零状态码表示异常终止
        }

        printInfo(c);
        Class<?> up = c.getSuperclass();
        Object o = null;
        try {
             o = up.newInstance();
        }catch(InstantiationException|IllegalAccessException e) {

        }
        printInfo(o.getClass());
        for(Class<?> face : up.getInterfaces()) {
            printInfo(face);
        }
    }

}

输出结果:

Class name: rtti.Mutt is interface? [false]
Canonical name: rtti.Mutt
Simple name: Mutt
------------------------------------------
Class name: rtti.Dog is interface? [false]
Canonical name: rtti.Dog
Simple name: Dog
------------------------------------------
Class name: rtti.Pet is interface? [true]
Canonical name: rtti.Pet
Simple name: Pet
------------------------------------------
Class name: rtti.Color is interface? [true]
Canonical name: rtti.Color
Simple name: Color
------------------------------------------

结果分析:

Class.forName(String ClassName): 使用类的全限定类名来获得该类Class对象的引用,若该类尚未加载,则加载该类。

getClass(): 如果已经拥有该类对象,可使用继承自Object类的getClass()方法获得该类Class对象。

getName(): 返回此Class对象所表示类的名称。

getCanonicalName(): 返回此Class对象所表示类的全限定类名。

getSimple(): 返回简称。

getInterfaces(): 返回Class对象数组,代表此类实现了的接口。

getSuperclass(): 返回class对象数组,代表此类的基类。

newInstance(): 创建此类的实例,此类必须有默认构造方法,此方法抛出异常。

基本上所有返回class对象的方法,其返回值都是Class<?>,因为编译器是不知道这类方法返回的Class对象的具体类型的。

3、示例三:类字面常量

class B {
    static {
        System.out.println("Initializing B");
    }
}
public class Demo3 {

    class A {
        {
            System.out.println("Initializing A");
        }
    }

    public static void main(String[] args) {

        Class<A> c1 = A.class;
        System.out.println(c1.getName());

        Class<?> c2 = null;
        try {
            c2 = Class.forName("rtti.B");
        }catch(ClassNotFoundException e) {
            e.printStackTrace();
            System.exit(1);
        }
        System.out.println(c2.getSimpleName());
    }

}

输出结果:

rtti.Demo3$A
Initializing B
B

结果分析:

可以明显的看出“.class”形式,比forName()更加高效。前者编译时,右值已知可以接受编译期检查;后者编译时,右值未知,还抛出异常。不过,".class"创建Class对象时不会进行类的初始化(前文说过,类加载过程中的一个阶段),forName()会进行类的初始化。

二、RTTI运行时类型识别

 运行时类型识别的三种表现形式:(看一下就行了,个人觉得无关紧要)

(1)RTTI确保类型转换的正确性

(2)代表对象类型的Class对象

(3)instanceof关键字

1、示例一:instanceof与“==”

class Base {}
class Derived extends Base {}
public class Demo4 {

    public static void main(String[] args) {
        Derived d = new Derived();
        System.out.println("d instanceof Base " + (d instanceof Base));
        System.out.println("d instanceof Derived " + (d instanceof Derived));
        System.out.println("Base.class.isInstance(d) " + Base.class.isInstance(d));
        System.out.println("Derived.class.isInstance(d) " + Derived.class.isInstance(d));
        System.out.println("d.getClass() == Base.class " + (d.getClass() == (Class<?>)Base.class));
        System.out.println("d.getClass() == Derived.class " + (d.getClass() == (Class<?>)Derived.class));
    }

}

输出结果:

d instanceof Base true
d instanceof Derived true
Base.class.isInstance(d) true
Derived.class.isInstance(d) true
d.getClass() == Base.class false
d.getClass() == Derived.class true

结果分析:instanceof与isInsatance()保持了类型的概念,x为子类示例结果也为true,即只要是同意类型即可。

三、功能强大的反射

1、示例一:反射基本接口的用法

import java.lang.reflect.*;
import java.util.Scanner;
import java.util.regex.*;;
public class ShowMembers {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String s = scanner.next();
        Pattern p = Pattern.compile("\\w+\\.");
        try {
            Class<?> c = Class.forName(s);
            System.out.println("Field:---------------------------------");
            for(Field f : c.getDeclaredFields()) {
                System.out.println(p.matcher(f.toString()).replaceAll(""));
            }
            System.out.println("Constructors:----------------------------");
            for(Constructor<?> con : c.getDeclaredConstructors()) {
                System.out.println(p.matcher(con.toString()).replaceAll(""));
            }
            System.out.println("Methods:------------------------------------------");
            for(Method m : c.getDeclaredMethods()) {
                System.out.println(p.matcher(m.toString()).replaceAll(""));
            }
        }catch(Exception e) {
            e.printStackTrace();
        }finally {
            scanner.close();
        }

    }
}

 输出结果:

java.io.FilterOutputStream
Field:---------------------------------
protected OutputStream out
Constructors:----------------------------
public FilterOutputStream(OutputStream)
Methods:------------------------------------------
public void write(byte[],int,int) throws IOException
public void write(byte[]) throws IOException
public void write(int) throws IOException
public void close() throws IOException
public void flush() throws IOException

第一行是输入

结果分析:

这是一个可以读取指定类中的字段、构造器以及方法的小程序。

getDeclaredFields():获取类中已经声明的字段,返回Field数组。

getDeclaredConstructors():获取类中已经声明的构造方法,返回Constructor<?>数组。

c.getDeclaredMethods():获取类中已经声明的方法,返回Method的数组。

Field类代表域、Constructor类代表构造方法、Method类代表方法。

这些基本的方法都属于Class类,是Class类对反射提供的支持。

2、使用反射越过编译器检查

import java.lang.reflect.*;
import java.util.*;
public class Reflect {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        List<Integer> l = new ArrayList<Integer>();
        l.add(1);
        Class<?> c = l.getClass();
        try {
            Method m = c.getMethod("add", Object.class);
            m.invoke(l, "test");
            System.out.println(l);
        }catch(Exception e) {
            e.printStackTrace();
        }

    }

}

 输出结果:

[1, test]

结果分析:可以看到利用反射,能够在编译器毫无防备的情况下狸猫换太子。

getMethod(): 可以获得Class对象的指定方法所代表的Method对象。

Method.invoke():可以调用该方法表示的对象。

原文地址:https://www.cnblogs.com/mgblogs/p/11478262.html

时间: 2024-10-28 16:37:02

第十四章 类型信息的相关文章

《JAVA编程思想》学习笔记——第十四章 类型信息

运行时类型信息使得你可以在程序运行时发现和使用类型信息. 主要有两种方式:一种是"传统的"RTTI, 它假定我们在编译时已经知道了所有的类型;另一种是"反射"机制,它允许我们在运行时发现和使用类的信息. Class对象 类是程序的一部分,每个类都有一个Class对象.换言之,每当编写并且编译一个新类,就会产生一个Class对象(更恰当的说,是被保存在一个同名的.class文件中).为了生成这个类的对象,运行这个程序的Java虚拟机(JVM)将使用被称为"类

第十四章 类型信息1(java的反射)

1.java语言的反射 1 package cn.itcast_01; 2 /* 1.通过反射获取构造方法并使用 3 * // 获取字节码文件对象 4 Class c = Class.forName("cn.itcast_01.Person"); 5 //获取指定的构造器对象(仅仅是获取到了) 6 该方法的形式参数就是构造方法的形式参数的.class对象(Class<?>... parameterTypes) 7 * parameterTypes 参数是 Class 对象的

c++第十四章-(类型强转换)

类型强转换有2种. class Company { public: Company(std::string theName,std::string theProduct); virtual void printInfo(); protected: std::string name; std::string product; }; Company::Company(std::string theName,std::string theProduct) { this->name = theName;

Java-第十四章-代参的方法(二)-编程实现,输入班里10名学生的身高,获得身高最高的学生要求对象数组类型方法

package com.ww.yzpA; public class Students { int No; int Height; } package com.ww.yzpA; public class Height { public Students getMaxHeigth(Students[] str) { Students A = new Students(); for (int i = 0; i < str.length; i++) { if (str[i].Height > A.He

javascript高级程序设计 第十四章--表单脚本

javascript高级程序设计 第十四章--表单脚本 在HTML中表单由<form>元素表示,在js中表单对应的是HTMLFormElement类型,这个类型也有很多属性和方法:取得表单元素的引用还是为它添加id特性,用DOM操作来获取表单元素:提交表单:把<input>或<button>元素的type特性设置为"submit",图像按钮把<input>元素的type特性设置为"image",也可以调用submit(

C和指针 (pointers on C)——第十四章:预处理器

第十四章 预处理器 我跳过了先进的指针主题的章节. 太多的技巧,太学科不适合今天的我.但我真的读,读懂.假设谁读了私下能够交流一下.有的小技巧还是非常有意思. 预处理器这一章的内容.大家肯定都用过.什么#include,#define #ifdef #undef这些,可是绝对用的不多.作为全面了解学C,还是应该都看一看. 预处理器使用方法非常讲究,用不好会失误,用好了会大大加快执行时速度(不是编译速度). 总结: C程序的第一个步骤就是预处理.预处理器共包括下面几个符号: 1.#define 定

Gradle 1.12用户指南翻译——第三十四章. JaCoCo 插件

本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Github上的地址: https://github.com/msdx/gradledoc/tree/1.12. 直接浏览双语版的文档请访问: http://gradledoc.qiniudn.com/1.12/userguide/userguide.html. 另外,Android 手机用户可通过我写的一个

Python基础教程(第十四章 网络编程)

本文内容全部出自<Python基础教程>第二版,在此分享自己的学习之路. ______欢迎转载:http://www.cnblogs.com/Marlowes/p/5538341.html______ Created on Marlowes 本章将会给读者展示一些例子,这些例子会使用多种Python的方法编写一个将网络(比如因特网)作为重要组成部分的程序.Python是一个很强大的网络编程工具,这么说有很多原因,首先,Python内有很多针对常见网络协议的库,在库顶部可以获得抽象层,这样就可以

第十四章——循环神经网络(Recurrent Neural Networks)(第二部分)

本章共两部分,这是第二部分: 第十四章--循环神经网络(Recurrent Neural Networks)(第一部分) 第十四章--循环神经网络(Recurrent Neural Networks)(第二部分) 14.4 深度RNN 堆叠多层cell是很常见的,如图14-12所示,这就是一个深度RNN. 图14-12 深度RNN(左),随时间展开(右) 在TensorFlow中实现深度RNN,需要创建多个cell并将它们堆叠到一个MultiRNNCell中.下面的代码创建了三个完全相同的cel