标签:验证 错误 获取对象 面向对象编程 原因 指针 模拟 本地 设计
运行时类型信息使得你可以在运行时发现和使用类型信息,主要有两种方式:
RTTI维护类型类型的信息,为多态机制的实现提供基础。
类型信息在运行时是通过Class
对象来表示的,完成的Class
对象包含了与类有关的信息。Class
对象就是用来创建所有“常规”对象的,Java使用Class
对象来执行RTTI
类是程序的一部分,每个类都有一个Class
对象,被保存在一个同名的.class
文件中。
Class
对象仅在需要的时候才会加载,static
初始化是在类加载时进行的。Class
对象是否已被加载过,如果尚未加载,默认的类加载器就会根据类名查找对应的.class
文件。想在运行时使用类型信息,必须获取对象的Class对象的引用:Class.forName("s2.A");
。该方法会自动初始化该Class对象,注意必须使用全限定名(包含包名)。
clz.getSimpleName()
// 获取全限定名
clz.getCanonicalName()
// 获取接口
clz.getInterfaces();
// 获取父类
clz.getSuperClass();
// 创建该类对象
clz.newInstance();
Java还提供了类字面常量的方式来生成对Class对象的引用:Class clz = A.class
。注意这种方式不会自动初始化该Class对象。
public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
Class.forName()
会自动初始化;使用A.class
不会自动初始化类static final int i = 1;
的值,则不需要初始化就可以被读取。static final
不足以保证是编译器常量,如static final int ii = new Random().nextInt();
。static
域不是final
的,那么访问之前要先进行链接和初始化。Java SE5之后,Class也可以支持范型了。
向Class引用添加范型语法的原因仅仅是为了提供编译期类型检查。
Class<String> clz = String.class;
String str1 = clz.cast("");
instanceOf
关键字,可以判断对象是否是某个类(及其父类)的实例。clz.isInstance()
方法接受一个对象,判断该对象是否是该clz
指向的类的实例。clz.isAssignableFrom()
方法接受一个Class
对象,判断该Class
对象是否是clz
自身或子类。public class Test {
public static void main(String[] args) throws Exception {
System.out.println(A.class.isAssignableFrom(C.class));
System.out.println(B.class.isAssignableFrom(C.class));
System.out.println(A.class.isInstance(new C()));
System.out.println(B.class.isInstance(new C()));
}
}
class A { }
interface B {}
class C extends A implements B {}
// Output:
// true
// true
// true
// true
使用工厂方法设计模式, 将对象的创建工作交给类自己去完成。 工厂方法可以被多态地调用, 从而为你创建恰当类型的对象。
反射在Java中是用来支持其他特性的,例如对象序列化和JavaBean。
代理是基本的设计模式之一,它是为你提供额外的或者不同的操作,而插入的用来代替“实际”对象的对象。这些操作通常设计与“实际”对象的通信,因此代理通常充当着中间人的角色。
public class Test {
public static void main(String[] args) throws Exception {
new RealObject().doSomething();
System.out.println("代理之后:");
new SimpleProxy(new RealObject()).doSomething();
}
}
interface MyInterface {
void doSomething();
}
class RealObject implements MyInterface {
@Override
public void doSomething() {
System.out.println("RealObject");
}
}
class SimpleProxy implements MyInterface {
private MyInterface myInterface;
public SimpleProxy(MyInterface myInterface) {
this.myInterface = myInterface;
}
// 代理后增加方法
@Override
public void doSomething() {
System.out.println("SimpleProxy");
myInterface.doSomething();
}
}
MyInterface myInterface = (MyInterface) Proxy.newProxyInstance(MyInterface.class.getClassLoader(), new Class[]{MyInterface.class}大专栏 编程思想14.类型信息oken punctuation">, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理方法");
return method.invoke(new RealObject(), args);
}
});
myInterface.doSomething();
null
的时候每次都要检查是否为null
,这是一件很麻烦的事。null
。instanceOf
来比较,还可以用equals
或==
来比较。null
没有区别,但在很多地方就不必执行额外的测试了,可以直接假设所有对象都是有效的。public class Test {
public static void main(String[] args) throws Exception {
// 在使用的时候可以直接使用而不会报错空指针
Person p = Person.NULL_PERSON;
System.out.println(p.toString());
}
}
// 空标记接口
interface Null {}
class Person {
void func() {
System.out.println("Person");
}
// 空对象
private static class NullPerson extends Person implements Null {
private NullPerson() {}
@Override
public String toString() {
return "NullPerson";
}
}
public static final Person NULL_PERSON = new NullPerson();
}
假设有不同的多个Person
的子类,我们相对每一个都创建一个空对象。无论何时,如果你需要一个空Person
对象,只需要调用newNullPerson()
并传递需要代理的Person
的类型。
public class Test {
public static Person newNullPerson(Class<? extends Person> type) {
return (Person) Proxy.newProxyInstance(NullPerson.NULL_PERSON.getClass().getClassLoader(), new Class[]{type}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理");
return method.invoke(NullPerson.NULL_PERSON, args);
}
});
}
public static void main(String[] args) throws Exception {
Person p = newNullPerson(Person.class);
p.func();
}
}
// 空标记接口
interface Null {}
// 父接口
interface Person {
void func();
}
// 空Person
class NullPerson implements Person, Null {
@Override
public void func() {
System.out.println("NullPerson");
}
public static final Person NULL_PERSON = new NullPerson();
private NullPerson() {}
}
空对象的逻辑变体是模拟对象和桩。与空对象一样,它们都表示在最终的程序中所使用的“实际”对象。但是,模拟对象和桩都只是假扮可以传递实际信息的存活对象,而不是像空对象那样可以成为null
的一种更加智能化的替代物。
模拟对象和桩之间的差异在于程度不同。模拟对象往往是轻量级和自测试的,通常很多模拟对象被创建出来是为了处理各种不同的测试情况。桩只是返回桩数据,它通常是重量级的,并且经常在测试之间被复用。桩可以根据它们被调用的方式,通过配置进行修改,因此桩是一 种复杂对象,它要做很多事。然而对于模拟对象,如果你需要做很多事情,通常会创建大量小而简单的模拟对象。
interface关键字的一种重要目标就是允许程序员隔离构件,进而降低耦合性。如果你编写接口,那么就可以实现这一目标,但是通过类型信息,这种耦合性还是会传播出去——接口并非是对解耦的一种无懈可击的保障。
public class Test {
public static void main(String[] args) {
A a = new B();
a.a();
// 我们需要的是用户使用接口,但是强制转型还是可以访问不存在于接口中的方法
((B) a).b();
}
}
interface A {
void a();
}
class B implements A {
@Override
public void a() {}
public void b() {}
}
如果程序员不使用接口而是子类,它们要对自己负责。即B a = new B();
代替A a = new B();
。
此时在此包外只能使用Hidden.newA()来获取对象,而且由于没有B类的信息,也无法强制转型。
class B implements A {
@Override
public void a() {}
public void b() {}
}
public class HiddenB {
public static A newA() {
return new B();
}
}
private
方法!如果知道方法名,你就可以在其Method
对象上调用setAccessible(true)
。final
域实际上在遭遇修改时是安全的。运行时系统会在不抛异常的情况下接受任何修改尝试,但是实际上不会发生任何修改。标签:验证 错误 获取对象 面向对象编程 原因 指针 模拟 本地 设计
原文地址:https://www.cnblogs.com/lijianming180/p/12041149.html