- 浏览: 123610 次
- 性别:
- 来自: 广州
文章分类
- 全部博客 (130)
- JUnit4学习 (0)
- Spring3.X学习 (2)
- 日记 (1)
- 文学类 (2)
- Java (15)
- Thingking In Java (11)
- org.apache.poi (4)
- XML (2)
- Log4j (1)
- Jar包收集 (2)
- ExtJs (1)
- 汇编语言 (11)
- 开发工具 (0)
- 电子书 (2)
- Oracle (6)
- Ajax (1)
- Jquery (2)
- myBatis (1)
- Spring2.5学习 (6)
- Tomcat (1)
- MyEclipse (1)
- JSP (1)
- Linux shell 脚本攻略 (7)
- Python3 (2)
- HTML5 (5)
- JavaScript (7)
- Hadoop-1.2.1 (2)
- Python2.7 (12)
- Django (3)
- 软件安装 (1)
- 高级Bash脚本编程指南 (7)
- Linux命令 (3)
- Ansible (2)
- MySQL (2)
- 病历 (1)
- 操作系统 (1)
- CSS (0)
- CSS3 (0)
- 面试题 (1)
最新评论
-
hw1287789687:
http://www.cnblogs.com/hccwu/p/ ...
Java获取真实的IP地址 -
liubey:
String ip = request.getHeader(& ...
Java获取真实的IP地址 -
bewithme:
我记得uploadify这破东西只能在chrome浏览器中才有 ...
Struts2结合Jquery.uploadify上传插件的应用 -
MrLee23:
http://mrlee23.iteye.com/admin/ ...
Struts2结合Jquery.uploadify上传插件的应用 -
crysik:
import com.eshore.ppm.model.com ...
Struts2结合Jquery.uploadify上传插件的应用
2013年6月25日 星期二 23时12分42秒
第十四章 类型信息
RTTI(Run-Time Type Identification)运行时类型识别
运行时类型信息使得你可以在程序运行时发现和使用类型信息。
本章将讨论Java是如何让我们在运行时识别对象和类的信息的,主要有两种方式:一种是“传统的”RTTI,它假定我们在编译时已经知道了所以的类型;另一种是“反射”机制,它允许我们在运行时发现和使用类的信息。
14.1 为什么需要 RTTI
package chapter14.typeinfo;
import java.util.*;
/*@name Shape.java
* @describe 14.1 为什么需要RTTI
* @since 2013-06-25 23:32
* @author 张彪
*/
abstract class Shape{
void draw(){System.out.println(this+" .draw()");}
abstract public String toString();
}
class Circle extends Shape{
public void draw(){System.out.println(this+" .draw()");}
public String toString(){return "Circle";}
}
class Square extends Shape{
public void draw(){System.out.println(this+" .draw()");}
public String toString(){return "Square";}
}
class Triangle extends Shape{
public void draw(){System.out.println(this+" .draw()");}
public String toString(){return "Triangle";}
}
public class Shapes {
public static void main(String[] args){
List<Shape> shapes=Arrays.asList(new Circle(),new Square(),new Triangle());
for(Shape s:shapes){
s.draw();
}
}
}
当从数组中取出元素时,这种容器---会自动将结果转型为Shape。这是RTTI最基本的使用形式,因为在Java中,所有的类型转换都是在运行时进行正确性检查的。这也是RTTI的 含义:在运行时,识别一个对象的类型。
接下来就是多态机制的事情了,Shape对象实际执行什么样的代码,是由引用所指向的具体对象Circle,Square,Triangle决定的。
14.2 Class对象
要理解RTTI在Java中的工作原理,首先必须知道类型信息在运行时是如何表示的。这项工作是由称为Class对象的特殊对象完成的,它包含了与类有关的信息。Class对象就是用来创建 类的所有的“常规”对象。
类加载器子系统实际上可以包含一条类加载器链,但是只有一个原生类加载器,它是JVM实现的一部分。原生类加载器加载的是所谓的可信类,包括JAVA API类。
所有类都是在对其第一次使用时,动态加载到JVM中的。当程序创建第一个对类的静态成员的引用时,就会加载这个类。
类加载器首先检查这个类的Class对象是否已经加载。如果尚未加载,默认的类加载器就会根据类名查找.class文件。
一旦某个类的Class对象被载入内存,它就被用来创建这个类的所有对象。 下面示范的程序可以说明这一点:
package chapter14.typeinfo;
class Candy{static{System.out.println("loading Candy");}}
class Gum{static{System.out.println("loading Gum");}}
class Cookie{static{System.out.println("loading Cookie");}}
public class SweetShop {
public static void main(String[] args){
System.out.println("inside main");
new Candy();
System.out.println("after creading candy");
try {
Class.forName("Gum");
} catch (ClassNotFoundException e) {
System.out.println("couldn't find Gum");
}
System.out.println("After Class.forName(\"Gum\")");
new Cookie();
System.out.println("After creating cookie");
}
}
/*inside main
loading Candy
after creading candy
couldn't find Gum
After Class.forName("Gum")
loading Cookie
After creating cookie*/
从输出结果可以看出,Class对象仅在需要的时候才被加载,static初始化实在类加载时进行的。
--------------------------------------------------------------------------------------------------------------
package chapter14.typeinfo.toy;
interface HasBatteries{}
interface Waterproof{}
interface Shoots{}
class Toy{
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() +" cc.isInterface()="+ cc.isInterface());
System.out.println("Simple Name="+cc.getSimpleName());
System.out.println("CanonicalName "+cc.getCanonicalName());
}
public static void main(String[] args){
Class c=null;
try {
c=Class.forName("chapter14.typeinfo.toy.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 {
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);
}
System.out.println("----------");
printInfo(obj.getClass());
}
}
/*Class name chapter14.typeinfo.toy.FancyToy cc.isInterface()=false
Simple Name=FancyToy
CanonicalName chapter14.typeinfo.toy.FancyToy
Class name chapter14.typeinfo.toy.HasBatteries cc.isInterface()=true
Simple Name=HasBatteries
CanonicalName chapter14.typeinfo.toy.HasBatteries
Class name chapter14.typeinfo.toy.Waterproof cc.isInterface()=true
Simple Name=Waterproof
CanonicalName chapter14.typeinfo.toy.Waterproof
Class name chapter14.typeinfo.toy.Shoots cc.isInterface()=true
Simple Name=Shoots
CanonicalName chapter14.typeinfo.toy.Shoots
----------
Class name chapter14.typeinfo.toy.Toy cc.isInterface()=false
Simple Name=Toy
CanonicalName chapter14.typeinfo.toy.Toy*/
注意:在传递给forName()的字符串中,必须使用全限定名(包含包名)。
printInfo()使用getName()来产生全限定的类名; getSuperClass()查询其直接基类;
Class.newInstance()方法是实现“虚拟构造器”的一种途径,虚拟构造器运行你声明:“我不知道你的确切类型,但是无论如何要正确地创建你自己。” 另外使用 newInstance()来创建的类,必须带有默认的构造器。在本章的稍后部分,你将会看到如何通过使用Java的反射API,用任意的构造器来动态地创建类的对象。
14.2.1 类字面常量
Java还提供了另一种方式生成对Class对象的引用,即使用类字面常量,对上述程序来说,就像下面这样:
FancyToy.class;
这样做不仅简单,而且更安全,因为它在编译时就会受到检查(因此不需要置于try语句块中),而且它根除了对forName()方法的调用,所以就要更高效。
类字面常量不仅可以用于普通的类,还可以应用于接口,数组及基本数据类型,另外对于基本数据类型的包装器类,还有一个标准字段TYPE。TYPE是一个引用,指向对应的基本 数据类型的Class对象。如下图所示:
==等价于==
boolean.class Boolean.TYPE
char.class Character.TYPE
byte.class BYTE.TYPE
........... ..........
这里建议使用.class的形式,以保持与普通类的一致性。当使用.class来创建对Class对象的引用时,不会自动地初始化该Class对象。
为了使用类而做的准备工作实际上包含三个步骤:
1) 加载 : 由类加载器执行
2) 链接 : 验证类中的字节码,为静态域分配存储空间
3) 初始化 :如果该类具有超类,则对其初始化,执行静态初始化器和静态初始化块。
package chapter14.typeinfo;
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){
Class initable=Initable.class;
System.out.println("after creating Initable ref");
System.out.println(Initable.staticFinal);
System.out.println(Initable.staticFinal2);
System.out.println(Initable2.staticNonFinal);
try {
Class initable3=Class.forName("chapter14.typeinfo.Initable3");
System.out.println(initable3.getAnnotations());
} catch (ClassNotFoundException e) {
System.out.println("not find");
}
System.out.println(Initable3.staticNonFinal);
}
}
从对initable的引用可以看到,仅使用.class语法来获得对类的引用不会引发初始化。但是,为了产生Class引用,Class.forName()立即就进行了初始化。
14.2.2 泛化的Class引用
通过使用泛型语法,可以让编译器强制执行额外的类型检查。
package chapter14.typeinfo;
/*@name Shape.java
* @describe 14.2.2 泛化的Class引用
* @since 2013-06-26 22:06
* @author 张彪
*/
public class GenericClassReferences {
public static void main(String[] args){
Class intClass=int.class;
Class<Integer> genericIntClass=int.class;
genericIntClass=Integer.class;
genericIntClass=Integer.TYPE;
intClass=double.class;
//genericIntClass=double.class; Illegal
//通配符"?"的使用
Class<?> int1Class=int.class;
//将通配符与extends关键字结合使用
Class<? extends Number> bounded=int.class;
bounded=double.class;
bounded=Number.class;
}
}
为了在使用泛化的Class引用时放松限制,我们可以使用通配符“?”,表示“任何事物”。因此我们可以在普通类中添加通配符,并产生相同的结果。
为了创建一个Class引用,它被限定为某种类型,或该类型的任何子类型。我们需要将通配符与extends关键字相结合,创建一个范围。
下面的示例使用了泛型类的语法:
package chapter14.typeinfo;
import java.util.*;
class CountedInteger{
private static long counter;
private final long id=counter++;
public String toString(){return Long.toString(id);}
}
public class FillList<T> {
private Class<T> type;
public FillList(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();
}
return result;
}
public static void main(String[] args){
FillList<CountedInteger> f1=new FillList<CountedInteger>(CountedInteger.class);
System.out.print(f1.create(15));
}
}
14.2.3 新的转型语法
Java SE5 还添加了用于Class引用的转型语法。即cast()方法:
package chapter14.typeinfo;
/*@name Shape.java
* @describe 14.2.3 新的转型语法 cast()
* @since 2013-06-26 23:06
* @author 张彪
*/
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;
}
}
cast()方法介绍参数对象,并将其转换为Class引用的类型。在方法在你编写泛型代码时非常有用(15章将讲到)。
14.3 类型转换前先做检查
迄今为止,我们已经知道的RTTI形式包括:
1) 传统的类型转换
2) 代表对象类型的Class对象
RTTI在Java中还有第三种形式,就是关键字instanceof。它返回一个布尔值,告诉我们对象是不是某个特定类型的实例。
if(x instanceof Dog)
((Dog)x).bark();
14.3.1 使用类字面常量
package chapter14.typeinfo.pets;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class LiteralPetCreator extends PetCreator {
@SuppressWarnings("unchecked")
public static final List<Class<? extends Pet>> allTypes = Collections
.unmodifiableList(Arrays.asList(Pet.class, Dog.class, Cat.class,
Rodent.class, Mutt.class, Pug.class, EgyptianMau.class,
Manx.class));
private static final List<Class<? extends Pet>> types = allTypes.subList(
allTypes.indexOf(Mutt.class), allTypes.size());
public List<Class<? extends Pet>> types() {
return types;
}
public static void main(String[] args) {
System.out.println(types);
}
}
14.3.2 动态的instanceof
package chapter14.typeinfo;
import net.mindview.*;
import java.util.*;
import chapter14.typeinfo.pets.Pet;
public class PetCount3 {
static class PetCount extends LinkedHashMap<Class<? extends Pet>, Integer>{
/*public PetCounter(){
super(MapData<K,V>.map(LiteralPetCreator.allTypes,0));
}*/
public void count(Pet pet){
for (Map.Entry<Class<? extends Pet>, Integer> pair:entrySet()) {
if(pair.getKey().isInstance(pet)){
put(pair.getKey(), pair.getValue()+1);
}
}
}
public String toString(){
StringBuilder result=new StringBuilder("{");
for (Map.Entry<Class<? extends Pet>, Integer> pair:entrySet()) {
result.append(pair.getKey().getSimpleName());
result.append("=");
result.append(pair.getValue());
result.append(",");
}
return result.toString();
}
}
}
14.3.3 递归计数
package net.mindview.util;
import java.util.HashMap;
import java.util.Map;
import chapter14.typeinfo.pets.Pet;
import chapter14.typeinfo.pets.Pets;
/*@name TypeCounter.java
* @describe 14.3.3 递归算法
* @since 2013-06-29 01:18
* @author 张彪
*/
public class TypeCounter extends HashMap<Class<?>, Integer>{
private Class<?> baseType;
public TypeCounter(Class<?> baseType){
this.baseType=baseType;
}
public void count(Object obj){
Class<?> type=obj.getClass();
if(!type.isAssignableFrom(type)){
throw new RuntimeException(obj+" incorrect type:"+type+" ,should be type or subtype of"+baseType);
}
countClass(type);
}
public void countClass(Class<?> type){
Integer quantity=get(type);
put(type, quantity==null ? 1:quantity+1);
Class<?> superClass=type.getSuperclass();
//isAssignableFrom(Class<?>) - 类 java.lang.Class 中的方法 ,判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口。
if(superClass !=null && baseType.isAssignableFrom(superClass)){
countClass(superClass);
}
}
public String toString(){
StringBuilder result=new StringBuilder("{");
for (Map.Entry<Class<?>, Integer> pair:entrySet()) {
result.append(pair.getKey().getSimpleName());
result.append("=");
result.append(pair.getValue());
result.append(",");
}
result.delete(result.length()-2,result.length());
result.append("}");
return result.toString();
}
public static void main(String[] args) {
TypeCounter counter=new TypeCounter(Pet.class);
for (Pet pet:Pets.arrayList(20)) {
counter.count(pet);
}
System.out.println();
System.out.println(counter);
}
}
14.4 注册工厂
package chapter14.typeinfo;
/*@name TypeCounter.java
* @describe 14.4 注册工厂
* @since 2013-06-29 1:47
* @author 张彪
*/
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import chapter14.typeinfo.factory.Factory;
class Part{
public String toString(){
return getClass().getSimpleName();
}
static List<Factory<? extends Part>> partFactories=new ArrayList<Factory<? extends Part>>();
static{
partFactories.add(new FueFilter.Factory());
partFactories.add(new AirFilter.Factory());
partFactories.add(new CabinAifFilter.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 FueFilter extends Filter{
public static class Factory implements chapter14.typeinfo.factory.Factory<FueFilter>{
public FueFilter create(){ return new FueFilter();}
}
}
class AirFilter extends Filter{
public static class Factory implements chapter14.typeinfo.factory.Factory<AirFilter>{
public AirFilter create(){return new AirFilter(); }
}
}
class CabinAifFilter extends Filter{
public static class Factory implements chapter14.typeinfo.factory.Factory<CabinAifFilter>{
public CabinAifFilter create(){return new CabinAifFilter();}
}
}
class OilFilter extends Filter{
public static class Factory implements chapter14.typeinfo.factory.Factory<OilFilter>{
public OilFilter create(){ return new OilFilter();}
}
}
class Belt extends Part{}
class FanBelt extends Belt{
public static class Factory implements chapter14.typeinfo.factory.Factory<FanBelt>{
public FanBelt create(){return new FanBelt();}
}
}
class GeneratorBelt extends Belt{
public static class Factory implements chapter14.typeinfo.factory.Factory<GeneratorBelt>{
public GeneratorBelt create(){ return new GeneratorBelt(); }
}
}
class PowerSteeringBelt extends Belt{
public static class Factory implements chapter14.typeinfo.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());
}
}
}
14.5 instanceof和Class的等价性
在类型查询时,以instanceof的形式(或isInstance()形式),他们与直接比较Class对象有一个很重要的差别。 如下示例:
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());
System.out.println("================");
test(new Derived());
}
}
由输出结果可知:instanceof 和 inInstance()生成的结果是一样的。但结论却不相同;instanceof保持了类型的概念,而如果用==比较实际的Class对象,就没有考虑到继承。
14.6 反射:运行时的类信息
如果不知道某个对象的确切类型,RTTI可以告诉你,但是有一个限制,这个类在编译时必须已知,这样RTTI才能识别它。
反射机制提供了一种机制----用来检查可用的方法,并返回方法名。Java通过JavaBean(第22章将详细介绍)提供了基于构件的编程架构。
Class类与java.lang.reflect类库一起对反射的概念进行了支持。该类库包含了Field,Method,以及Constructor类(每个类都实现了Member接口)。这些类型的对象是由JVM在运行时 创建的,用于表示未知类里对应的成员。 这样就可以使用Constructor创建新的对象,用get()和set()方法存取和修改与Field对象关联的字段,用invoke()方法调用与Method对象关联 的方法。
RTTI和反射直接的正确区别在于:对RTTI来说,编译器在编译时打开和检查.class文件。(话句话说,我们可以用普通方式调用对象的所有方法。)而对于反射机制来说,.class文件 在编译时是不可获取的,所以是在运行时打开和检查.class文件。
14.6.1 类方法提取器
Class<?> c = Class.forName("chapter14.typeinfo.ShowMethods");
Method[] methods = c.getMethods();
Constructor[] ctors = c.getConstructors();
if (methods.length>0) {
for (Method m : methods) {
System.out.println(p.matcher(m.toString()).replaceAll(""));
}
for (Constructor cs : ctors) {
System.out.println(p.matcher(cs.toString()).replaceAll(""));
}
lines = methods.length + ctors.length;
}
14.7 动态代理
代理是基本的设计模式之一,它是为了提供额外的或不同的操作,而插入的用来代替“对象”的对象。这些操作通常涉及与“实际”对象的通信,因此代理通常充当着中间人的角色。
下面来展示代理结构的简单示例:
package chapter14.typeinfo;
/*@name ShowMethods.java
* @describe 14.7 動態代理
* @since 2013-07-01 21:48
* @author 张彪
*/
interface Interface{
public void doSomething();
public void somethingElse(String arg);
}
class RealObject implements Interface{
public void doSomething(){}
public void somethingElse(String arg){
System.out.println("somethingElse "+arg);
}
}
class SimpleProxy implements Interface{
private Interface proxiedInterface;
public SimpleProxy(Interface proxiedInterface){
this.proxiedInterface=proxiedInterface;
}
public void doSomething(){
System.out.println("SimpleProxy.doSomething");
proxiedInterface.doSomething();
}
public void somethingElse(String arg){
System.out.println("somethingElse "+arg);
proxiedInterface.somethingElse(arg);
}
}
public class SimpleProxyDemo{
public static void consumer(Interface interface1 ){
interface1.doSomething();
interface1.somethingElse("bonobo");
}
public static void main(String[] args) {
consumer(new RealObject());
consumer(new SimpleProxy(new RealObject()));
}
}
因为consumer()接受的Interface,所以它无法知道正在获得的到底是RealObject还是SimpleProxy,因为这二者都是实现了Interface,但是SimpleProxy已经被插入到客户端和 RealObject之间,因此它会执行操作,然后调用RealObject上相同的方法。
Java动态代理比代理的思想更想去迈进了一步。
package chapter14.typeinfo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/*@name ShowMethods.java
* @describe 14.7 通过代理中的参数进行方法过滤
* @since 2013-07-01 23:03
* @author 张彪
*/
class MethodSelector implements InvocationHandler{
private Object proxied;
public MethodSelector(Object proxied){
this.proxied=proxied;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(method.getName().equals("interesting")){
System.out.println("Proxy detected the interesting method");
}
return method.invoke(proxy, args);
}
}
interface SomeMethod{
void boring1();
void boring2();
void boring3();
void interesting(String args);
}
class Implementation implements SomeMethod{
public void boring1(){System.out.println("boring1");}
public void boring2(){System.out.println("boring2");}
public void boring3(){System.out.println("boring3");}
public void interesting(String args){
System.out.println("interesting "+args);
}
}
public class SelectionMethods {
public static void main(String[] args) {
Implementation it=new Implementation();
SomeMethod sm=(SomeMethod)Proxy.newProxyInstance(SomeMethod.class.getClassLoader(),new Class[]{SomeMethod.class},new MethodSelector(it));
sm.boring1();
sm.boring2();
sm.boring3();
sm.interesting("bonobo");
}
}
14.8 空对象
当你使用内置的null表示对象时,在每次使用引用时都必须测试其是否为null,这显得枯燥。
有时引入空对象的思想很有用,它可以接受传递给他的所代表的对象的消息,但是将返回表示为实质上并不存在的任何“真实”对象的值。通过这种方式,你可以假设所有对象都是有效的, 而不必去浪费变成精力其检测null。
package chapter14.typeinfo;
public class Person {
public final String first;
public final String last;
public final String address;
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 nULLPerson=new NullPerson();
}
注意:此处Java代码implement Null,初次见到这种写法,IDE还报错了。。。。。。。
14.8.1 模拟对象与桩
空对象的逻辑变体是模拟对象和桩。与空对象一样,他们都表示在最终的程序中所使用的“实际”对象。但是模拟对象和桩只是假扮可以传递实际信息的存活对象,而不是像空对 象那样可以成为null的一种更加智能化的替代物。
14.9 接口与类型信息
interface关键字的一种重要目标就是允许程序员隔离构件,进而降低耦合度。如果你编写接口,那就可以实现这一目标,但是通过类型信息,这种耦合度还是会传播出去-----接口并非是对 耦合的一种无懈可击的保障。
package chapter14.typeinfo;
import java.lang.reflect.Method;
import chapter14.typeinfo.interfacea.A;
import chapter14.typeinfo.packageaccess.Hiddenc;
public class HiddenImplementation {
public static void main(String[] args) throws Exception{
A a=Hiddenc.makeA();
a.f();
//Reflect still allows us to call g()
callHiddenMethod(a,"g");
//下面这几个方法仍然能调用
callHiddenMethod(a,"w");
callHiddenMethod(a,"v");
callHiddenMethod(a,"u");
}
static void callHiddenMethod(Object a, String methodName)throws Exception{
Method g=a.getClass().getDeclaredMethod(methodName);
g.setAccessible(true);
g.invoke(a);
}
}
从上面的例子来看,通过使用反射机制,仍旧可以达到并调用所有方法,甚至是private方法!如果知道方法名,就可以在其Method对象上setAccessible(true)。
尽管将代码编译后进行发布,也不能解决问题。因为只需运行javap,一个随JDK发布的反编译器即可突破这一限制。 如命令行:
javap -private C
-private 表示所有成员都应该显示,甚至是包括私有成员。
因此任何人都可以获取你最私有的方法的名字和签名,然后调用他们。
内部私有类和匿名类都无法阻止反射到达并调用那些非公共访问权限的方法。对于域来说,的确如此,即便是private域。
14.10 总结
不要太早的关注程序的效率问题,这个是个诱人的陷进,最好先让程序运行起来,然后再考虑它的速度。如果要解决效率问题,可以使用profiler。
2013-07-02 01:23 记 @tangxiacun.tianhequ.guanzhou
第十四章 类型信息
RTTI(Run-Time Type Identification)运行时类型识别
运行时类型信息使得你可以在程序运行时发现和使用类型信息。
本章将讨论Java是如何让我们在运行时识别对象和类的信息的,主要有两种方式:一种是“传统的”RTTI,它假定我们在编译时已经知道了所以的类型;另一种是“反射”机制,它允许我们在运行时发现和使用类的信息。
14.1 为什么需要 RTTI
package chapter14.typeinfo;
import java.util.*;
/*@name Shape.java
* @describe 14.1 为什么需要RTTI
* @since 2013-06-25 23:32
* @author 张彪
*/
abstract class Shape{
void draw(){System.out.println(this+" .draw()");}
abstract public String toString();
}
class Circle extends Shape{
public void draw(){System.out.println(this+" .draw()");}
public String toString(){return "Circle";}
}
class Square extends Shape{
public void draw(){System.out.println(this+" .draw()");}
public String toString(){return "Square";}
}
class Triangle extends Shape{
public void draw(){System.out.println(this+" .draw()");}
public String toString(){return "Triangle";}
}
public class Shapes {
public static void main(String[] args){
List<Shape> shapes=Arrays.asList(new Circle(),new Square(),new Triangle());
for(Shape s:shapes){
s.draw();
}
}
}
当从数组中取出元素时,这种容器---会自动将结果转型为Shape。这是RTTI最基本的使用形式,因为在Java中,所有的类型转换都是在运行时进行正确性检查的。这也是RTTI的 含义:在运行时,识别一个对象的类型。
接下来就是多态机制的事情了,Shape对象实际执行什么样的代码,是由引用所指向的具体对象Circle,Square,Triangle决定的。
14.2 Class对象
要理解RTTI在Java中的工作原理,首先必须知道类型信息在运行时是如何表示的。这项工作是由称为Class对象的特殊对象完成的,它包含了与类有关的信息。Class对象就是用来创建 类的所有的“常规”对象。
类加载器子系统实际上可以包含一条类加载器链,但是只有一个原生类加载器,它是JVM实现的一部分。原生类加载器加载的是所谓的可信类,包括JAVA API类。
所有类都是在对其第一次使用时,动态加载到JVM中的。当程序创建第一个对类的静态成员的引用时,就会加载这个类。
类加载器首先检查这个类的Class对象是否已经加载。如果尚未加载,默认的类加载器就会根据类名查找.class文件。
一旦某个类的Class对象被载入内存,它就被用来创建这个类的所有对象。 下面示范的程序可以说明这一点:
package chapter14.typeinfo;
class Candy{static{System.out.println("loading Candy");}}
class Gum{static{System.out.println("loading Gum");}}
class Cookie{static{System.out.println("loading Cookie");}}
public class SweetShop {
public static void main(String[] args){
System.out.println("inside main");
new Candy();
System.out.println("after creading candy");
try {
Class.forName("Gum");
} catch (ClassNotFoundException e) {
System.out.println("couldn't find Gum");
}
System.out.println("After Class.forName(\"Gum\")");
new Cookie();
System.out.println("After creating cookie");
}
}
/*inside main
loading Candy
after creading candy
couldn't find Gum
After Class.forName("Gum")
loading Cookie
After creating cookie*/
从输出结果可以看出,Class对象仅在需要的时候才被加载,static初始化实在类加载时进行的。
--------------------------------------------------------------------------------------------------------------
package chapter14.typeinfo.toy;
interface HasBatteries{}
interface Waterproof{}
interface Shoots{}
class Toy{
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() +" cc.isInterface()="+ cc.isInterface());
System.out.println("Simple Name="+cc.getSimpleName());
System.out.println("CanonicalName "+cc.getCanonicalName());
}
public static void main(String[] args){
Class c=null;
try {
c=Class.forName("chapter14.typeinfo.toy.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 {
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);
}
System.out.println("----------");
printInfo(obj.getClass());
}
}
/*Class name chapter14.typeinfo.toy.FancyToy cc.isInterface()=false
Simple Name=FancyToy
CanonicalName chapter14.typeinfo.toy.FancyToy
Class name chapter14.typeinfo.toy.HasBatteries cc.isInterface()=true
Simple Name=HasBatteries
CanonicalName chapter14.typeinfo.toy.HasBatteries
Class name chapter14.typeinfo.toy.Waterproof cc.isInterface()=true
Simple Name=Waterproof
CanonicalName chapter14.typeinfo.toy.Waterproof
Class name chapter14.typeinfo.toy.Shoots cc.isInterface()=true
Simple Name=Shoots
CanonicalName chapter14.typeinfo.toy.Shoots
----------
Class name chapter14.typeinfo.toy.Toy cc.isInterface()=false
Simple Name=Toy
CanonicalName chapter14.typeinfo.toy.Toy*/
注意:在传递给forName()的字符串中,必须使用全限定名(包含包名)。
printInfo()使用getName()来产生全限定的类名; getSuperClass()查询其直接基类;
Class.newInstance()方法是实现“虚拟构造器”的一种途径,虚拟构造器运行你声明:“我不知道你的确切类型,但是无论如何要正确地创建你自己。” 另外使用 newInstance()来创建的类,必须带有默认的构造器。在本章的稍后部分,你将会看到如何通过使用Java的反射API,用任意的构造器来动态地创建类的对象。
14.2.1 类字面常量
Java还提供了另一种方式生成对Class对象的引用,即使用类字面常量,对上述程序来说,就像下面这样:
FancyToy.class;
这样做不仅简单,而且更安全,因为它在编译时就会受到检查(因此不需要置于try语句块中),而且它根除了对forName()方法的调用,所以就要更高效。
类字面常量不仅可以用于普通的类,还可以应用于接口,数组及基本数据类型,另外对于基本数据类型的包装器类,还有一个标准字段TYPE。TYPE是一个引用,指向对应的基本 数据类型的Class对象。如下图所示:
==等价于==
boolean.class Boolean.TYPE
char.class Character.TYPE
byte.class BYTE.TYPE
........... ..........
这里建议使用.class的形式,以保持与普通类的一致性。当使用.class来创建对Class对象的引用时,不会自动地初始化该Class对象。
为了使用类而做的准备工作实际上包含三个步骤:
1) 加载 : 由类加载器执行
2) 链接 : 验证类中的字节码,为静态域分配存储空间
3) 初始化 :如果该类具有超类,则对其初始化,执行静态初始化器和静态初始化块。
package chapter14.typeinfo;
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){
Class initable=Initable.class;
System.out.println("after creating Initable ref");
System.out.println(Initable.staticFinal);
System.out.println(Initable.staticFinal2);
System.out.println(Initable2.staticNonFinal);
try {
Class initable3=Class.forName("chapter14.typeinfo.Initable3");
System.out.println(initable3.getAnnotations());
} catch (ClassNotFoundException e) {
System.out.println("not find");
}
System.out.println(Initable3.staticNonFinal);
}
}
从对initable的引用可以看到,仅使用.class语法来获得对类的引用不会引发初始化。但是,为了产生Class引用,Class.forName()立即就进行了初始化。
14.2.2 泛化的Class引用
通过使用泛型语法,可以让编译器强制执行额外的类型检查。
package chapter14.typeinfo;
/*@name Shape.java
* @describe 14.2.2 泛化的Class引用
* @since 2013-06-26 22:06
* @author 张彪
*/
public class GenericClassReferences {
public static void main(String[] args){
Class intClass=int.class;
Class<Integer> genericIntClass=int.class;
genericIntClass=Integer.class;
genericIntClass=Integer.TYPE;
intClass=double.class;
//genericIntClass=double.class; Illegal
//通配符"?"的使用
Class<?> int1Class=int.class;
//将通配符与extends关键字结合使用
Class<? extends Number> bounded=int.class;
bounded=double.class;
bounded=Number.class;
}
}
为了在使用泛化的Class引用时放松限制,我们可以使用通配符“?”,表示“任何事物”。因此我们可以在普通类中添加通配符,并产生相同的结果。
为了创建一个Class引用,它被限定为某种类型,或该类型的任何子类型。我们需要将通配符与extends关键字相结合,创建一个范围。
下面的示例使用了泛型类的语法:
package chapter14.typeinfo;
import java.util.*;
class CountedInteger{
private static long counter;
private final long id=counter++;
public String toString(){return Long.toString(id);}
}
public class FillList<T> {
private Class<T> type;
public FillList(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();
}
return result;
}
public static void main(String[] args){
FillList<CountedInteger> f1=new FillList<CountedInteger>(CountedInteger.class);
System.out.print(f1.create(15));
}
}
14.2.3 新的转型语法
Java SE5 还添加了用于Class引用的转型语法。即cast()方法:
package chapter14.typeinfo;
/*@name Shape.java
* @describe 14.2.3 新的转型语法 cast()
* @since 2013-06-26 23:06
* @author 张彪
*/
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;
}
}
cast()方法介绍参数对象,并将其转换为Class引用的类型。在方法在你编写泛型代码时非常有用(15章将讲到)。
14.3 类型转换前先做检查
迄今为止,我们已经知道的RTTI形式包括:
1) 传统的类型转换
2) 代表对象类型的Class对象
RTTI在Java中还有第三种形式,就是关键字instanceof。它返回一个布尔值,告诉我们对象是不是某个特定类型的实例。
if(x instanceof Dog)
((Dog)x).bark();
14.3.1 使用类字面常量
package chapter14.typeinfo.pets;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class LiteralPetCreator extends PetCreator {
@SuppressWarnings("unchecked")
public static final List<Class<? extends Pet>> allTypes = Collections
.unmodifiableList(Arrays.asList(Pet.class, Dog.class, Cat.class,
Rodent.class, Mutt.class, Pug.class, EgyptianMau.class,
Manx.class));
private static final List<Class<? extends Pet>> types = allTypes.subList(
allTypes.indexOf(Mutt.class), allTypes.size());
public List<Class<? extends Pet>> types() {
return types;
}
public static void main(String[] args) {
System.out.println(types);
}
}
14.3.2 动态的instanceof
package chapter14.typeinfo;
import net.mindview.*;
import java.util.*;
import chapter14.typeinfo.pets.Pet;
public class PetCount3 {
static class PetCount extends LinkedHashMap<Class<? extends Pet>, Integer>{
/*public PetCounter(){
super(MapData<K,V>.map(LiteralPetCreator.allTypes,0));
}*/
public void count(Pet pet){
for (Map.Entry<Class<? extends Pet>, Integer> pair:entrySet()) {
if(pair.getKey().isInstance(pet)){
put(pair.getKey(), pair.getValue()+1);
}
}
}
public String toString(){
StringBuilder result=new StringBuilder("{");
for (Map.Entry<Class<? extends Pet>, Integer> pair:entrySet()) {
result.append(pair.getKey().getSimpleName());
result.append("=");
result.append(pair.getValue());
result.append(",");
}
return result.toString();
}
}
}
14.3.3 递归计数
package net.mindview.util;
import java.util.HashMap;
import java.util.Map;
import chapter14.typeinfo.pets.Pet;
import chapter14.typeinfo.pets.Pets;
/*@name TypeCounter.java
* @describe 14.3.3 递归算法
* @since 2013-06-29 01:18
* @author 张彪
*/
public class TypeCounter extends HashMap<Class<?>, Integer>{
private Class<?> baseType;
public TypeCounter(Class<?> baseType){
this.baseType=baseType;
}
public void count(Object obj){
Class<?> type=obj.getClass();
if(!type.isAssignableFrom(type)){
throw new RuntimeException(obj+" incorrect type:"+type+" ,should be type or subtype of"+baseType);
}
countClass(type);
}
public void countClass(Class<?> type){
Integer quantity=get(type);
put(type, quantity==null ? 1:quantity+1);
Class<?> superClass=type.getSuperclass();
//isAssignableFrom(Class<?>) - 类 java.lang.Class 中的方法 ,判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口。
if(superClass !=null && baseType.isAssignableFrom(superClass)){
countClass(superClass);
}
}
public String toString(){
StringBuilder result=new StringBuilder("{");
for (Map.Entry<Class<?>, Integer> pair:entrySet()) {
result.append(pair.getKey().getSimpleName());
result.append("=");
result.append(pair.getValue());
result.append(",");
}
result.delete(result.length()-2,result.length());
result.append("}");
return result.toString();
}
public static void main(String[] args) {
TypeCounter counter=new TypeCounter(Pet.class);
for (Pet pet:Pets.arrayList(20)) {
counter.count(pet);
}
System.out.println();
System.out.println(counter);
}
}
14.4 注册工厂
package chapter14.typeinfo;
/*@name TypeCounter.java
* @describe 14.4 注册工厂
* @since 2013-06-29 1:47
* @author 张彪
*/
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import chapter14.typeinfo.factory.Factory;
class Part{
public String toString(){
return getClass().getSimpleName();
}
static List<Factory<? extends Part>> partFactories=new ArrayList<Factory<? extends Part>>();
static{
partFactories.add(new FueFilter.Factory());
partFactories.add(new AirFilter.Factory());
partFactories.add(new CabinAifFilter.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 FueFilter extends Filter{
public static class Factory implements chapter14.typeinfo.factory.Factory<FueFilter>{
public FueFilter create(){ return new FueFilter();}
}
}
class AirFilter extends Filter{
public static class Factory implements chapter14.typeinfo.factory.Factory<AirFilter>{
public AirFilter create(){return new AirFilter(); }
}
}
class CabinAifFilter extends Filter{
public static class Factory implements chapter14.typeinfo.factory.Factory<CabinAifFilter>{
public CabinAifFilter create(){return new CabinAifFilter();}
}
}
class OilFilter extends Filter{
public static class Factory implements chapter14.typeinfo.factory.Factory<OilFilter>{
public OilFilter create(){ return new OilFilter();}
}
}
class Belt extends Part{}
class FanBelt extends Belt{
public static class Factory implements chapter14.typeinfo.factory.Factory<FanBelt>{
public FanBelt create(){return new FanBelt();}
}
}
class GeneratorBelt extends Belt{
public static class Factory implements chapter14.typeinfo.factory.Factory<GeneratorBelt>{
public GeneratorBelt create(){ return new GeneratorBelt(); }
}
}
class PowerSteeringBelt extends Belt{
public static class Factory implements chapter14.typeinfo.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());
}
}
}
14.5 instanceof和Class的等价性
在类型查询时,以instanceof的形式(或isInstance()形式),他们与直接比较Class对象有一个很重要的差别。 如下示例:
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());
System.out.println("================");
test(new Derived());
}
}
由输出结果可知:instanceof 和 inInstance()生成的结果是一样的。但结论却不相同;instanceof保持了类型的概念,而如果用==比较实际的Class对象,就没有考虑到继承。
14.6 反射:运行时的类信息
如果不知道某个对象的确切类型,RTTI可以告诉你,但是有一个限制,这个类在编译时必须已知,这样RTTI才能识别它。
反射机制提供了一种机制----用来检查可用的方法,并返回方法名。Java通过JavaBean(第22章将详细介绍)提供了基于构件的编程架构。
Class类与java.lang.reflect类库一起对反射的概念进行了支持。该类库包含了Field,Method,以及Constructor类(每个类都实现了Member接口)。这些类型的对象是由JVM在运行时 创建的,用于表示未知类里对应的成员。 这样就可以使用Constructor创建新的对象,用get()和set()方法存取和修改与Field对象关联的字段,用invoke()方法调用与Method对象关联 的方法。
RTTI和反射直接的正确区别在于:对RTTI来说,编译器在编译时打开和检查.class文件。(话句话说,我们可以用普通方式调用对象的所有方法。)而对于反射机制来说,.class文件 在编译时是不可获取的,所以是在运行时打开和检查.class文件。
14.6.1 类方法提取器
Class<?> c = Class.forName("chapter14.typeinfo.ShowMethods");
Method[] methods = c.getMethods();
Constructor[] ctors = c.getConstructors();
if (methods.length>0) {
for (Method m : methods) {
System.out.println(p.matcher(m.toString()).replaceAll(""));
}
for (Constructor cs : ctors) {
System.out.println(p.matcher(cs.toString()).replaceAll(""));
}
lines = methods.length + ctors.length;
}
14.7 动态代理
代理是基本的设计模式之一,它是为了提供额外的或不同的操作,而插入的用来代替“对象”的对象。这些操作通常涉及与“实际”对象的通信,因此代理通常充当着中间人的角色。
下面来展示代理结构的简单示例:
package chapter14.typeinfo;
/*@name ShowMethods.java
* @describe 14.7 動態代理
* @since 2013-07-01 21:48
* @author 张彪
*/
interface Interface{
public void doSomething();
public void somethingElse(String arg);
}
class RealObject implements Interface{
public void doSomething(){}
public void somethingElse(String arg){
System.out.println("somethingElse "+arg);
}
}
class SimpleProxy implements Interface{
private Interface proxiedInterface;
public SimpleProxy(Interface proxiedInterface){
this.proxiedInterface=proxiedInterface;
}
public void doSomething(){
System.out.println("SimpleProxy.doSomething");
proxiedInterface.doSomething();
}
public void somethingElse(String arg){
System.out.println("somethingElse "+arg);
proxiedInterface.somethingElse(arg);
}
}
public class SimpleProxyDemo{
public static void consumer(Interface interface1 ){
interface1.doSomething();
interface1.somethingElse("bonobo");
}
public static void main(String[] args) {
consumer(new RealObject());
consumer(new SimpleProxy(new RealObject()));
}
}
因为consumer()接受的Interface,所以它无法知道正在获得的到底是RealObject还是SimpleProxy,因为这二者都是实现了Interface,但是SimpleProxy已经被插入到客户端和 RealObject之间,因此它会执行操作,然后调用RealObject上相同的方法。
Java动态代理比代理的思想更想去迈进了一步。
package chapter14.typeinfo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/*@name ShowMethods.java
* @describe 14.7 通过代理中的参数进行方法过滤
* @since 2013-07-01 23:03
* @author 张彪
*/
class MethodSelector implements InvocationHandler{
private Object proxied;
public MethodSelector(Object proxied){
this.proxied=proxied;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(method.getName().equals("interesting")){
System.out.println("Proxy detected the interesting method");
}
return method.invoke(proxy, args);
}
}
interface SomeMethod{
void boring1();
void boring2();
void boring3();
void interesting(String args);
}
class Implementation implements SomeMethod{
public void boring1(){System.out.println("boring1");}
public void boring2(){System.out.println("boring2");}
public void boring3(){System.out.println("boring3");}
public void interesting(String args){
System.out.println("interesting "+args);
}
}
public class SelectionMethods {
public static void main(String[] args) {
Implementation it=new Implementation();
SomeMethod sm=(SomeMethod)Proxy.newProxyInstance(SomeMethod.class.getClassLoader(),new Class[]{SomeMethod.class},new MethodSelector(it));
sm.boring1();
sm.boring2();
sm.boring3();
sm.interesting("bonobo");
}
}
14.8 空对象
当你使用内置的null表示对象时,在每次使用引用时都必须测试其是否为null,这显得枯燥。
有时引入空对象的思想很有用,它可以接受传递给他的所代表的对象的消息,但是将返回表示为实质上并不存在的任何“真实”对象的值。通过这种方式,你可以假设所有对象都是有效的, 而不必去浪费变成精力其检测null。
package chapter14.typeinfo;
public class Person {
public final String first;
public final String last;
public final String address;
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 nULLPerson=new NullPerson();
}
注意:此处Java代码implement Null,初次见到这种写法,IDE还报错了。。。。。。。
14.8.1 模拟对象与桩
空对象的逻辑变体是模拟对象和桩。与空对象一样,他们都表示在最终的程序中所使用的“实际”对象。但是模拟对象和桩只是假扮可以传递实际信息的存活对象,而不是像空对 象那样可以成为null的一种更加智能化的替代物。
14.9 接口与类型信息
interface关键字的一种重要目标就是允许程序员隔离构件,进而降低耦合度。如果你编写接口,那就可以实现这一目标,但是通过类型信息,这种耦合度还是会传播出去-----接口并非是对 耦合的一种无懈可击的保障。
package chapter14.typeinfo;
import java.lang.reflect.Method;
import chapter14.typeinfo.interfacea.A;
import chapter14.typeinfo.packageaccess.Hiddenc;
public class HiddenImplementation {
public static void main(String[] args) throws Exception{
A a=Hiddenc.makeA();
a.f();
//Reflect still allows us to call g()
callHiddenMethod(a,"g");
//下面这几个方法仍然能调用
callHiddenMethod(a,"w");
callHiddenMethod(a,"v");
callHiddenMethod(a,"u");
}
static void callHiddenMethod(Object a, String methodName)throws Exception{
Method g=a.getClass().getDeclaredMethod(methodName);
g.setAccessible(true);
g.invoke(a);
}
}
从上面的例子来看,通过使用反射机制,仍旧可以达到并调用所有方法,甚至是private方法!如果知道方法名,就可以在其Method对象上setAccessible(true)。
尽管将代码编译后进行发布,也不能解决问题。因为只需运行javap,一个随JDK发布的反编译器即可突破这一限制。 如命令行:
javap -private C
-private 表示所有成员都应该显示,甚至是包括私有成员。
因此任何人都可以获取你最私有的方法的名字和签名,然后调用他们。
内部私有类和匿名类都无法阻止反射到达并调用那些非公共访问权限的方法。对于域来说,的确如此,即便是private域。
14.10 总结
不要太早的关注程序的效率问题,这个是个诱人的陷进,最好先让程序运行起来,然后再考虑它的速度。如果要解决效率问题,可以使用profiler。
2013-07-02 01:23 记 @tangxiacun.tianhequ.guanzhou
- chapter14.rar (16.1 KB)
- 下载次数: 0
发表评论
-
第十三章 字符串
2013-08-06 00:50 9442013年8月1日 星期四 21时05分59秒 第十三章 字 ... -
第十二章 通过异常处理错误
2013-08-01 21:04 7632013年7月10日 星期三 00时04分21秒 第十二章 ... -
第十一章 持有对象
2013-07-09 00:49 9592013年6月24日 星期一 20时57分09秒 第十一章 ... -
第十章 内部类
2013-06-24 20:47 10212013年6月23日 星期日 16时50分56秒 第十章 内 ... -
第九章 接口
2013-06-23 16:46 8822013年6月20日 星期四 21时41分40秒 第九章 接 ... -
第八章 多态
2013-06-19 23:06 7122013年6月17日 星期一 23 ... -
Thinking in Java Fourth Edition Source Code
2013-06-17 23:10 551Thinking in Java F ... -
第七章 复用类
2013-06-17 22:36 5972013年6月16日 星期日 21时06分54秒 第七章 复 ... -
第六章 访问权限控制
2013-06-16 21:05 8922013年6月16日 星期日 11时10分46秒 第六章 访 ... -
第五章 初始化与清理
2013-06-16 10:58 6372013年6月15日 星期六 16 ...
评论