Java编程思想之十四 类型信息

时间:2022-09-02 10:46:11

第十四章 类型信息

运行时类型信息使得你可以在程序运行时发现和使用类型信息

14.1 为什么需要RTTI

面向对象编程中基本的目的是:让代码只操作对基类的引用。

多态:

import java.util.*;

abstract class Shape {
void draw() { System.out.println(this + ".draw()"); }
abstract public String toString();
} class Circle extends Shape {
public String toString() { return "Circle"; }
} class Square extends Shape {
public String toString() { return "Square"; }
} class Triangle extends Shape {
public String toString() { return "Triangle"; }
} public class Shapes {
public static void main(String[] args) {
List<Shape> shapeList = Arrays.asList(
new Circle(), new Square(), new Triangle()
);
for(Shape shape : shapeList)
shape.draw();
}
} /* Output:
Circle.draw()
Square.draw()
Triangle.draw()
*///:~

Rtti基本使用形式:所有类型转换都是在运行时进行正确性检查的。在运行时识别一个对象的类型。

14.2 Class对象

每当编写并且编译一个新类,就会产生一个Class对象。为了生成这个类的对象,运行这个程序的Java虚拟机(JVM)将使用被称为"类加载器"的子系统。

类加载器子系统实际上可以包含一条加载器链,但是只有一个原生类加载器,它是JVM实现的一部分。原生类加载器加载的所谓的可信类,它们通常是从本地盘加载的。

所有类都在第一个使用时,动态加载到JVM中。

Java程序在它开始运行之前并非被完全加载,其各个部分是在必须时才加载的。

类加载器首先检查这个类的class对象是否已经加载。如果没有加载,默认的类加载器就会根据类名查找.class文件。

//: typeinfo/toys/ToyTest.java
// Testing class Class.
package toys; interface HasBatteries
{
} interface Waterproof
{
} interface Shoots
{
} class Toy
{
// Comment out the following default constructor
// to see NoSuchMethodError from (*1*)
Toy()
{
} Toy(int i)
{
}
} class FancyToy extends Toy
implements HasBatteries, Waterproof, Shoots
{
FancyToy()
{
super(1);
}
} public class ToyTest
{
static void printInfo(Class cc)
{
System.out.println("Class name: " + cc.getName() +
" is interface? [" + cc.isInterface() + "]");
System.out.println("Simple name: " + cc.getSimpleName());
System.out.println("Canonical name : " + cc.getCanonicalName());
} public static void main(String[] args)
{
Class c = null;
try
{
c = Class.forName("toys.FancyToy");
}
catch (ClassNotFoundException e)
{
System.out.println("Can't find FancyToy");
System.exit(1);
}
printInfo(c);
for (Class face : c.getInterfaces())
printInfo(face);
Class up = c.getSuperclass();
Object obj = null;
try
{
// Requires default constructor:
obj = up.newInstance();
}
catch (InstantiationException e)
{
System.out.println("Cannot instantiate");
System.exit(1);
}
catch (IllegalAccessException e)
{
System.out.println("Cannot access");
System.exit(1);
}
printInfo(obj.getClass());
}
} /* Output:
Class name: typeinfo.toys.FancyToy is interface? [false]
Simple name: FancyToy
Canonical name : typeinfo.toys.FancyToy
Class name: typeinfo.toys.HasBatteries is interface? [true]
Simple name: HasBatteries
Canonical name : typeinfo.toys.HasBatteries
Class name: typeinfo.toys.Waterproof is interface? [true]
Simple name: Waterproof
Canonical name : typeinfo.toys.Waterproof
Class name: typeinfo.toys.Shoots is interface? [true]
Simple name: Shoots
Canonical name : typeinfo.toys.Shoots
Class name: typeinfo.toys.Toy is interface? [false]
Simple name: Toy
Canonical name : typeinfo.toys.Toy
*///:~

14.2.1 类字面常量

Java还提供了一个方法来生成对Class对象的引用,即使用类字面常量

FancyToy.class;

类字面常量不仅可以应用于普通的类,也可以应用于接口、数组、基本数据类型。

对于基本数据类型的包装器类,还有一个标准字段TYPE。TYPE字段是一个引用,指向对应的基本数据类型的Class对象。

当使用.class创建对象的引用时,不会自动初始化该class对象。

为了使用类而做的准备工作实际包含三个步骤:

  1. 加载:这是由类加载器执行的。该步骤将查找字节码,并从这些字节码中创建一个Class对象。
  2. 链接:在链接阶段将验证类中的字节码,为静态域分配存储空间,并且如果必须的话,将解析这个类创建的对其他类的所有引用。
  3. 初始化:如果该类具有超类,则对其初始化,执行静态初始化器和静态初始化块。
//: typeinfo/ClassInitialization.java
import java.util.*; class Initable {
static final int staticFinal = 47;//编译期常量,不需要进行初始化就可以读取
static final int staticFinal2 =
ClassInitialization.rand.nextInt(1000);
static {
System.out.println("Initializing Initable");
}
} class Initable2 {
static int staticNonFinal = 147;
static {
System.out.println("Initializing Initable2");
}
} class Initable3 {
static int staticNonFinal = 74;
static {
System.out.println("Initializing Initable3");
}
} public class ClassInitialization {
public static Random rand = new Random(47);
public static void main(String[] args) throws Exception {
Class initable = Initable.class;
System.out.println("After creating Initable ref");
// Does not trigger initialization:
System.out.println(Initable.staticFinal);
// Does trigger initialization:
System.out.println(Initable.staticFinal2);
// Does trigger initialization:
System.out.println(Initable2.staticNonFinal);
Class initable3 = Class.forName("Initable3");
System.out.println("After creating Initable3 ref");
System.out.println(Initable3.staticNonFinal);
}
} /* Output:
After creating Initable ref
47
Initializing Initable
258
Initializing Initable2
147
Initializing Initable3
After creating Initable3 ref
74
*///:~

如果一个static域不是final的,那么在对它访问时,总是要求在它被读取前,先进行链接(分配存储空间)和初始化(初始化存储空间)

14.2.2 泛化的Class引用

Class引用总是指向某个Class对象,它可以制造类的实例,并包含可作用于这些实例的所有方法代码。它还包含该类的静态成员,因此,Class引用表达的就是它所指向的对象的确切类型,而该对象便是Class类的一个对象。

通过允许你对Class引用所指向的Class对象的类型进行限定,将它的类型变得更具体一些。

public class GenericClassReferences
{
public static void main(String[] args)
{
Class intClass = int.class;
Class<Integer> genericIntClass = int.class;//只能指向具体的类型
genericIntClass = Integer.class; // Same thing
intClass = double.class;
//genericIntClass = double.class; // Illegal
}
} ///:~

通配符:? 表示任何事物。

public class WildcardClassReferences {
public static void main(String[] args) {
Class<?> intClass = int.class;
intClass = double.class;
}
} ///:~

Class优于平凡得class,即便他们是等价得。Class的好处表达你知道你选择的是非具体的版本。

将通配符和extends关键字结合使用:

public class BoundedClassReferences {
public static void main(String[] args) {
Class<? extends Number> bounded = int.class;
bounded = double.class;
bounded = Number.class;
// Or anything else derived from Number.
}
} ///:~

下面的例子,它存储了一个引用,稍后又产生了一个List,填充这个List对象使用newInstance()方法:

import java.util.*;

class CountedInteger {
private static long counter;
private final long id = counter++;
public String toString() { return Long.toString(id); }
} public class FilledList<T> {
private Class<T> type;
public FilledList(Class<T> type) { this.type = type; }
public List<T> create(int nElements) {
List<T> result = new ArrayList<T>();
try {
for(int i = 0; i < nElements; i++)
result.add(type.newInstance());
} catch(Exception e) {
throw new RuntimeException(e);
}
return result;
}
public static void main(String[] args) {
FilledList<CountedInteger> fl =
new FilledList<CountedInteger>(CountedInteger.class);
System.out.println(fl.create(15));
}
} /* Output:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
*///:~

newInstance()将返回对象的确切类型:

public class GenericToy
{
public static void main(String[] args) throws Exception
{
Class<B> b = B.class;
System.out.println("a Name:" + b.getName());
B b1 = b.newInstance();
System.out.println(b);
A a = (A)b.getSuperclass().newInstance();
System.out.println("getSuperclass.Name:"+a);
A a1 = b.newInstance();
System.out.println(a1); Object o = b.getSuperclass();
System.out.println(o);
}
}
class A{
static {
System.out.println("AAAA");
}
public String toString() {
return "a";
}
}
class B extends A{
static {
System.out.println("BBBB");
}
public String toString() {
return "b";
}
}
14.2.3 新的转型语法

Class引用的转型语法cast()方法:接受参入参数对象,转换为引用类型

class Building {}
class House extends Building {} public class ClassCasts {
public static void main(String[] args) {
Building b = new House();
Class<House> houseType = House.class;
House h = houseType.cast(b);
h = (House)b; // ... or just do this.
}
} ///:~

Class.asSubclass():允许你将一个类对象转型为更加具体的类型。

14.3 类型转换前先做检查

我们已知的RTTI形式包括:

  1. 传统的类型转换
  2. 代表对象的类型的CLass对象。查询Class对象获取允许时所需要的信息。
  3. 关键字instanceof。它返回一个布尔值,告诉我们对象是不是某个特定类型的实例。

instanceof只可将其与命名空间类型进行比较,而不能与Class对象作比较。

14.4 注册工厂

使用工厂方法设计模式,将对象的创建工作交给类自己去完成,工厂方法可以被多态的调用,从而为你创建恰当类型的对象。

使用工厂创建对象:

//: typeinfo/RegisteredFactories.java
// Registering Class Factories in the base class.
import factory.*;
import java.util.*; class Part {
public String toString() {
return getClass().getSimpleName();
}
static List<Factory<? extends Part>> partFactories =
new ArrayList<Factory<? extends Part>>();
static {
// Collections.addAll() gives an "unchecked generic
// array creation ... for varargs parameter" warning.
partFactories.add(new FuelFilter.Factory());
partFactories.add(new AirFilter.Factory());
partFactories.add(new CabinAirFilter.Factory());
partFactories.add(new OilFilter.Factory());
partFactories.add(new FanBelt.Factory());
partFactories.add(new PowerSteeringBelt.Factory());
partFactories.add(new GeneratorBelt.Factory());
}
private static Random rand = new Random(47);
public static Part createRandom() {
int n = rand.nextInt(partFactories.size());
return partFactories.get(n).create();
}
} class Filter extends Part {} class FuelFilter extends Filter {
// Create a Class Factory for each specific type:
public static class Factory
implements factory.Factory<FuelFilter> {
public FuelFilter create() { return new FuelFilter(); }
}
} class AirFilter extends Filter {
public static class Factory
implements factory.Factory<AirFilter> {
public AirFilter create() { return new AirFilter(); }
}
} class CabinAirFilter extends Filter {
public static class Factory
implements factory.Factory<CabinAirFilter> {
public CabinAirFilter create() {
return new CabinAirFilter();
}
}
} class OilFilter extends Filter {
public static class Factory
implements factory.Factory<OilFilter> {
public OilFilter create() { return new OilFilter(); }
}
} class Belt extends Part {} class FanBelt extends Belt {
public static class Factory
implements factory.Factory<FanBelt> {
public FanBelt create() { return new FanBelt(); }
}
} class GeneratorBelt extends Belt {
public static class Factory
implements factory.Factory<GeneratorBelt> {
public GeneratorBelt create() {
return new GeneratorBelt();
}
}
} class PowerSteeringBelt extends Belt {
public static class Factory
implements factory.Factory<PowerSteeringBelt> {
public PowerSteeringBelt create() {
return new PowerSteeringBelt();
}
}
} public class RegisteredFactories {
public static void main(String[] args) {
for(int i = 0; i < 10; i++)
System.out.println(Part.createRandom());
}
} /* Output:
GeneratorBelt
CabinAirFilter
GeneratorBelt
AirFilter
PowerSteeringBelt
CabinAirFilter
FuelFilter
PowerSteeringBelt
PowerSteeringBelt
FuelFilter
*///:~

14.5 instanceof与Class的等价性

在查询类型信息时,以instanceof的形式与直接比较Class对象有一个很重要的差别,下面看看这些差别:

//: typeinfo/FamilyVsExactType.java
// The difference between instanceof and class class Base {}
class Derived extends Base {} public class FamilyVsExactType {
static void test(Object x) {
System.out.println("Testing x of type " + x.getClass());
System.out.println("x instanceof Base " + (x instanceof Base));
System.out.println("x instanceof Derived "+ (x instanceof Derived));
System.out.println("Base.isInstance(x) "+ Base.class.isInstance(x));
System.out.println("Derived.isInstance(x) " +
Derived.class.isInstance(x));
System.out.println("x.getClass() == Base.class " +
(x.getClass() == Base.class));
System.out.println("x.getClass() == Derived.class " +
(x.getClass() == Derived.class));
System.out.println("x.getClass().equals(Base.class)) "+
(x.getClass().equals(Base.class)));
System.out.println("x.getClass().equals(Derived.class)) " +
(x.getClass().equals(Derived.class)));
}
public static void main(String[] args) {
test(new Base());
test(new Derived());
}
} /* Output:
Testing x of type class typeinfo.Base
x instanceof Base true
x instanceof Derived false
Base.isInstance(x) true
Derived.isInstance(x) false
x.getClass() == Base.class true
x.getClass() == Derived.class false
x.getClass().equals(Base.class)) true
x.getClass().equals(Derived.class)) false
Testing x of type class typeinfo.Derived
x instanceof Base true
x instanceof Derived true
Base.isInstance(x) true
Derived.isInstance(x) true
x.getClass() == Base.class false
x.getClass() == Derived.class true
x.getClass().equals(Base.class)) false
x.getClass().equals(Derived.class)) true
*///:~

14.6 反射:运行时的类信息

在编译时,编译器必须知道所要通过RTTI来处理的类。

反射提供了一种机制——用来检查可用的方法,并返回方法名。

人们想要在运行时获取类的信息的另一个动机,希望在跨网络的平台上创建和运行对象的能力。这被称为远程方法调用(RMI),它允许将一个Java程序分布到多台机器上。

Class类与java.lang.reflect类库一起对反射概念进行了支持。这些类型的对象都是又JVM在运行时创建的,用以表示未知类里对应的成员。

当通过反射与一个未知类型的对象打交道时,JVM只是简单的检查这个对象,看它属于哪个特定的类。在用它做其他事情之前必须先加载那个类的Class对象。因此,那个类的.Class文件对于JVM来说必须是可获取的:要么在本地机器上,要么可以通过网络获取。所以RTTI和反射之间真正的区别只在于du:对RTTI来说,编译器在编译时打开和检查.class文件,对于反射来说:.class在编译时是不可获取的,所有在运行时打开和检查.class文件。

14.6.1 类方法提取器

反射机制提供了一种方法,使我们能够编写可以自动展示完整接口的简单工具:


14.7 动态代理

代理是基本的设计模式之一,它是你为了提供额外的或不同的操作,而插入的用来代替实际对象的对象。

interface Interface {
void doSomething();
void somethingElse(String arg);
}
class RealObject implements Interface {
public void doSomething() { System.out.println("doSomething"); }
public void somethingElse(String arg) {
System.out.println("somethingElse " + arg);
}
} class SimpleProxy implements Interface {
private Interface proxied;
public SimpleProxy(Interface proxied) {
this.proxied = proxied;
}
public void doSomething() {
System.out.println("SimpleProxy doSomething");
proxied.doSomething();
}
public void somethingElse(String arg) {
System.out.println("SimpleProxy somethingElse " + arg);
proxied.somethingElse(arg);
}
} class SimpleProxyDemo {
public static void consumer(Interface iface) {
iface.doSomething();
iface.somethingElse("bonobo");
}
public static void main(String[] args) {
consumer(new RealObject());
consumer(new SimpleProxy(new RealObject()));
}
}

Java的动态代理可以动态创建并动态地处理对所代理方法的调用。在动态代理上所做的所有调用都会被重定向到单一的调用处理器上,它的工作是揭示调用的类型并确定相应的对策。

import java.lang.reflect.*;

class DynamicProxyHandler implements InvocationHandler {
private Object proxied;
public DynamicProxyHandler(Object proxied) {
this.proxied = proxied;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("**** proxy: " + proxy.getClass() +
", method: " + method + ", args: " + args);
if(args != null)
for(Object arg : args)
System.out.println(" " + arg);
return method.invoke(proxied, args);
}
} class SimpleDynamicProxy {
public static void consumer(Interface iface) {
iface.doSomething();
iface.somethingElse("bonobo");
}
public static void main(String[] args) {
RealObject real = new RealObject();
consumer(real);
// Insert a proxy and call again:
Interface proxy = (Interface)Proxy.newProxyInstance(
Interface.class.getClassLoader(),
new Class[]{ Interface.class },
new DynamicProxyHandler(real));
consumer(proxy);
}
}

动态代理可以将所有调用重定向到调用处理器,因此通常会向调用处理器的构造器传递一个实际对象的引用,从而使得调用处理器在执行其中介任务时,可以将请求转发。

14.8 空对象

引入空对象的思想是很有用的,它可以接收传递给它的所代表的对象的消息,但是将返回表示为实际上并不存在的任何真实对象的值。通过这种方式,你可以假设所有的对象都是有效的,而不必浪费时间去检查null。

即使空对象可以响应实际对象可以响应的所有消息,任需要测试是否为空:

public interface Operation {
String description();
void command();
}
class Person {
public final String first;
public final String last;
public final String address;
// etc.
public Person(String first, String last, String address){
this.first = first;
this.last = last;
this.address = address;
}
public String toString() {
return "Person: " + first + " " + last + " " + address;
}
public static class NullPerson
extends Person implements Null {
private NullPerson() { super("None", "None", "None"); }
public String toString() { return "NullPerson"; }
}
public static final Person NULL = new NullPerson();
}
class Position {
private String title;
private Person person;
public Position(String jobTitle, Person employee) {
title = jobTitle;
person = employee;
if(person == null)
person = Person.NULL;
}
public Position(String jobTitle) {
title = jobTitle;
person = Person.NULL;
}
public String getTitle() { return title; }
public void setTitle(String newTitle) {
title = newTitle;
}
public Person getPerson() { return person; }
public void setPerson(Person newPerson) {
person = newPerson;
if(person == null)
person = Person.NULL;
}
public String toString() {
return "Position: " + title + " " + person;
}
}
//: typeinfo/Staff.java
import java.util.*; public class Staff extends ArrayList<Position> {
public void add(String title, Person person) {
add(new Position(title, person));
}
public void add(String... titles) {
for(String title : titles)
add(new Position(title));
}
public Staff(String... titles) { add(titles); }
public boolean positionAvailable(String title) {
for(Position position : this)
if(position.getTitle().equals(title) &&
position.getPerson() == Person.NULL)
return true;
return false;
}
public void fillPosition(String title, Person hire) {
for(Position position : this)
if(position.getTitle().equals(title) &&
position.getPerson() == Person.NULL) {
position.setPerson(hire);
return;
}
throw new RuntimeException(
"Position " + title + " not available");
}
public static void main(String[] args) {
Staff staff = new Staff("President", "CTO",
"Marketing Manager", "Product Manager",
"Project Lead", "Software Engineer",
"Software Engineer", "Software Engineer",
"Software Engineer", "Test Engineer",
"Technical Writer");
staff.fillPosition("President",
new Person("Me", "Last", "The Top, Lonely At"));
staff.fillPosition("Project Lead",
new Person("Janet", "Planner", "The Burbs"));
if(staff.positionAvailable("Software Engineer"))
staff.fillPosition("Software Engineer",
new Person("Bob", "Coder", "Bright Light City"));
System.out.println(staff);
}
} /* Output:
[Position: President Person: Me Last The Top, Lonely At, Position: CTO NullPerson, Position: Marketing Manager NullPerson, Position: Product Manager NullPerson, Position: Project Lead Person: Janet Planner The Burbs, Position: Software Engineer Person: Bob Coder Bright Light City, Position: Software Engineer NullPerson, Position: Software Engineer NullPerson, Position: Software Engineer NullPerson, Position: Test Engineer NullPerson, Position: Technical Writer NullPerson]
*///:~

使用命令模式:

//: typeinfo/Robot.java
import java.util.*;
public interface Robot {
String name();
String model();
List<Operation> operations();
class Test {
public static void test(Robot r) {
if(r instanceof Null)
System.out.println("[Null Robot]");
System.out.println("Robot name: " + r.name());
System.out.println("Robot model: " + r.model());
for(Operation operation : r.operations()) {
System.out.println(operation.description());
operation.command();
}
}
}
} ///:~
//: typeinfo/SnowRemovalRobot.java
import java.util.*; public class SnowRemovalRobot implements Robot {
private String name;
public SnowRemovalRobot(String name) {this.name = name;}
public String name() { return name; }
public String model() { return "SnowBot Series 11"; }
public List<Operation> operations() {
return Arrays.asList(
new Operation() {
public String description() {
return name + " can shovel snow";
}
public void command() {
System.out.println(name + " shoveling snow");
}
},
new Operation() {
public String description() {
return name + " can chip ice";
}
public void command() {
System.out.println(name + " chipping ice");
}
},
new Operation() {
public String description() {
return name + " can clear the roof";
}
public void command() {
System.out.println(name + " clearing roof");
}
}
);
}
public static void main(String[] args) {
Robot.Test.test(new SnowRemovalRobot("Slusher"));
}
} /* Output:
Robot name: Slusher
Robot model: SnowBot Series 11
Slusher can shovel snow
Slusher shoveling snow
Slusher can chip ice
Slusher chipping ice
Slusher can clear the roof
Slusher clearing roof
*///:~
//: typeinfo/NullRobot.java
// Using a dynamic proxy to create a Null Object.
import java.lang.reflect.*;
import java.util.*;
class NullRobotProxyHandler implements InvocationHandler {
private String nullName;
private Robot proxied = new NRobot();
NullRobotProxyHandler(Class<? extends Robot> type) {
nullName = type.getSimpleName() + " NullRobot";
}
private class NRobot implements Null, Robot {
public String name() { return nullName; }
public String model() { return nullName; }
public List<Operation> operations() {
return Collections.emptyList();
}
}
public Object
invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return method.invoke(proxied, args);
}
}
public class NullRobot {
public static Robot
newNullRobot(Class<? extends Robot> type) {
return (Robot)Proxy.newProxyInstance(
NullRobot.class.getClassLoader(),
new Class[]{ Null.class, Robot.class },
new NullRobotProxyHandler(type));
}
public static void main(String[] args) {
Robot[] bots = {
new SnowRemovalRobot("SnowBee"),
newNullRobot(SnowRemovalRobot.class)
};
for(Robot bot : bots)
Robot.Test.test(bot);
}
} /* Output:
Robot name: SnowBee
Robot model: SnowBot Series 11
SnowBee can shovel snow
SnowBee shoveling snow
SnowBee can chip ice
SnowBee chipping ice
SnowBee can clear the roof
SnowBee clearing roof
[Null Robot]
Robot name: SnowRemovalRobot NullRobot
Robot model: SnowRemovalRobot NullRobot
*///:~

14.8.1 模拟对象与桩

空对象的逻辑变体是模拟对象和桩。模拟对象和桩都只是假扮可以传递实际信息的存活对象,而不是像空对象那样可以称为null的一种更加智能化的替代品。

14.9 接口与类型信息

interface关键字的一种重要目标就是允许程序员隔离构建,进而降低耦合性。通过类型信息,这种耦合性还是会传播出去——接口并非是对解耦的一种无懈可击的保障。

package interfacea;

public interface A {
void f();
}
import interfacea.*;

class B implements A {
public void f() {}
public void g() {}
} public class InterfaceViolation {
public static void main(String[] args) {
A a = new B();
a.f();
// a.g(); // Compile error
System.out.println(a.getClass().getName());
if(a instanceof B) {
B b = (B)a;
b.g();
}
}
}

最简单的方式是对实现使用包访问权限,这样在包外部的客户端就不能看见它了。

package packageaccess;
import interfacea.*;
class C implements A {
public void f() { System.out.println("public C.f()"); }
public void g() { System.out.println("public C.g()"); }
void u() { System.out.println("package C.u()"); }
protected void v() { System.out.println("protected C.v()"); }
private void w() { System.out.println("private C.w()"); }
} public class HiddenC {
public static A makeA() { return new C(); }
}

这里makeA返回C类型,但在包在并不能使用到C。但是反射却仍旧可以调用:

import interfacea.*;
import packageaccess.*;
import java.lang.reflect.*; public class HiddenImplementation {
public static void main(String[] args) throws Exception {
A a = HiddenC.makeA();
a.f();
System.out.println(a.getClass().getName());
// Compile error: cannot find symbol 'C':
/* if(a instanceof C) {
C c = (C)a;
c.g();
} */
// Oops! Reflection still allows us to call g():
callHiddenMethod(a, "g");
// And even methods that are less accessible!
callHiddenMethod(a, "u");
callHiddenMethod(a, "v");
callHiddenMethod(a, "w");
}
static void callHiddenMethod(Object a, String methodName)
throws Exception {
Method g = a.getClass().getDeclaredMethod(methodName);
g.setAccessible(true);
g.invoke(a);
}
} /* Output:
public C.f()
typeinfo.packageaccess.C
public C.g()
package C.u()
protected C.v()
private C.w()
*///:~

即使是内部类,反思仍旧可以调用到:

import interfacea.*;
class InnerA {
private static class C implements A {
public void f() { System.out.println("public C.f()"); }
public void g() { System.out.println("public C.g()"); }
void u() { System.out.println("package C.u()"); }
protected void v() { System.out.println("protected C.v()"); }
private void w() { System.out.println("private C.w()"); }
}
public static A makeA() { return new C(); }
} public class InnerImplementation {
public static void main(String[] args) throws Exception {
A a = InnerA.makeA();
a.f();
System.out.println(a.getClass().getName());
// Reflection still gets into the private class:
HiddenImplementation.callHiddenMethod(a, "g");
HiddenImplementation.callHiddenMethod(a, "u");
HiddenImplementation.callHiddenMethod(a, "v");
HiddenImplementation.callHiddenMethod(a, "w");
}
}

匿名类也一样:

import interfacea.*;
class AnonymousA {
public static A makeA() {
return new A() {
public void f() { System.out.println("public C.f()"); }
public void g() { System.out.println("public C.g()"); }
void u() { System.out.println("package C.u()"); }
protected void v() { System.out.println("protected C.v()"); }
private void w() { System.out.println("private C.w()"); }
};
}
}
public class AnonymousImplementation {
public static void main(String[] args) throws Exception {
A a = AnonymousA.makeA();
a.f();
System.out.println(a.getClass().getName());
// Reflection still gets into the anonymous class:
HiddenImplementation.callHiddenMethod(a, "g");
HiddenImplementation.callHiddenMethod(a, "u");
HiddenImplementation.callHiddenMethod(a, "v");
HiddenImplementation.callHiddenMethod(a, "w");
}
} /* Output:
public C.f()
AnonymousA$1
public C.g()
package C.u()
protected C.v()
private C.w()
*///:~

Java编程思想之十四 类型信息的更多相关文章

  1. 《Java编程思想》笔记14&period;类型信息

    运行时类型信息使得你可以在运行时发现和使用类型信息,主要有两种方式: "传统的"RTTI,它假定我们在编译时已经知道了所有的类型: "反射"机制,它允许我们在运 ...

  2. Java编程思想——第14章 类型信息(一)

    运行时类型信息使得你可以在程序运行时发现和使用类型信息.Java是如何让我们在运行时识别对象和类的信息得呢? 主要有两种方式:1.传统RTTI,他假定我们在编译期间已经知道了所有类型:2.反射,它允许 ...

  3. Java编程思想——第14章 类型信息(二)反射

    六.反射:运行时的类信息 我们已经知道了,在编译时,编译器必须知道所有要通过RTTI来处理的类.而反射提供了一种机制——用来检查可用的方法,并返回方法名.区别就在于RTTI是处理已知类的,而反射用于处 ...

  4. Java编程思想学习&lpar;十四&rpar; 枚举

    关键字enum可以将一组具名的值有限集合创建一种为新的类型,而这些具名的值可以作为常规的程序组件使用. 基本enum特性 调用enum的values()方法可以遍历enum实例,values()方法返 ...

  5. JAVA编程思想(第四版)学习笔记----4&period;8 switch&lpar;知识点已更新&rpar;

    switch语句和if-else语句不同,switch语句可以有多个可能的执行路径.在第四版java编程思想介绍switch语句的语法格式时写到: switch (integral-selector) ...

  6. 《JAVA编程思想》第四版 PDF

    感谢,参考:https://www.cnblogs.com/buwuliao/p/8073211.html 一.链接: 中文版: https://pan.baidu.com/s/1d07Kp4 密码: ...

  7. 《JAVA编程思想》第四版 PDF 下载 中文版和英文版 高清PDF扫描带书签

    一.链接: 中文版: https://pan.baidu.com/s/1d07Kp4 密码:x2cd 英文版: https://pan.baidu.com/s/1boOSdAZ 密码: rwgm 文件 ...

  8. Java编程思想学习&lpar;十五&rpar; 注解

    注解Annotation又叫元数据,是JDK5中引入的一种以通用格式为程序提供配置信息的方式.使用注解Annotation可以使元数据写在程序源码中,使得代码看起来简洁,同时编译器也提供了对注解Ann ...

  9. Java编程思想之十八 枚举类型

    关键字enum可以将一组具名的值的有限集合创建为一种新的类型, 而这些具名的值可以作为常规的程序组件使用.这是一种非常有用的功能. 18.1 基本enum特性 创建enum时,编译器会为你生成一个相关 ...

随机推荐

  1. 深入理解Javascript--作用域和赋值操作

    作用域作为一个最基础的功能存在于各种编程语言中,它使得我们的编程更加灵活有趣.其基础功能就是存储变量中的值,然后可以对值进行访问和修改. 可能我们都知道作用域的一些概念,以及其一些扩展的一些内容闭包等 ...

  2. 批处理命令——goto 和 &colon;

    谈起goto,相信大家应该想到的是面向过程编程.其实,这就相当于当有人向你谈起class,意味着你就懂得面向对象编程.如果你不懂,那么你们的沟通将会很困难.不懂我说的啥意思吗?请参见曾经分享王路的一篇 ...

  3. WPF UserControl响应窗体的PreviewKeyDown事件

    目的 在UserControl页面实现通过快捷键打开新建窗口 实现过程 监听Window窗体的PreviewKeyDown 其实,使用KeyDown事件也是可以的 页面代码 <Window x: ...

  4. Nginx配置文件及模块解析

    一.Nginx是什么? Nginx是一个基于c语言开发的高性能http服务器及反向代理服务器.由俄罗斯的程序设计师Igor Sysoev所开发,官方测试nginx能够支支撑5万并发链接,并且cpu.内 ...

  5. u-boot&lpar;四&rpar;命令实现

    目录 u-boot(四)命令实现 分析run_command 小结 自定义一个命令 代码 makefile title: u-boot(四)命令实现 tags: linux date: 2018-09 ...

  6. HTML&amp&semi;javaSkcript&amp&semi;CSS&amp&semi;jQuery&amp&semi;ajax(八)

    一. <!DOCTYPE html><html><head><meta charset="utf-8"><tiitle> ...

  7. 模块移除 命令rmmod 的实现

    // rmmod.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include &lt ...

  8. 一些JavaScript基本函数

    1.document.write(”");为 输出语句 2.JS中的注释为// 3.传统的HTML文档顺序是:document->html->(head,body) 4.一个浏览 ...

  9. Python &plus; selenium &plus; unittest装饰器 &commat;classmethod

    前言 前面讲到unittest里面setUp可以在每次执行用例前执行,这样有效的减少了代码量,但是有个弊端,比如打开浏览器操作,每次执行用例时候都会重新打开,这样就会浪费很多时间. 于是就想是不是可以 ...

  10. 七、linux目录结构知识---实战

    1.企业面试题:一个100M的磁盘分区,分别写入1k文件,及写入1M的文件,分别可以写多少个? 一块磁盘被分区格式化成系统文件后,有Inode和Block:一个文件一般占用一个Inode和一个Bloc ...