Java编程思想 第14章类型信息 学习笔记
其他
2021-03-02 07:05:02
阅读次数: 0
为什么需要RTTI
RTTI,Run-Time Type Identification
运行时类型信息使得你可以在程序运行时发现和使用类型信息
当需要知道某个泛化引用的确切类型,并根据不同的类型,进行不同的处理。
Class对象
Class
对象就是用来创建类的所有的“常规”对象的
Java使用Class对象来执行其RTTI操作
类是程序的一部分,每个类都有一个Class
对象。换言之,每当编写并编译了一个新类,就会产生一个Class
对象。为了生成这个类的对象,运行这个程序的Java虚拟机(JVM)将使用被称为“类加载器”的子系统
所有类都是在对其第一次使用时,动态加载到JVM的。当程序创建第一个对类的静态成员的引用时,就会加载这个类。这个证明构造器也是类的静态方法,即使在构造器之前并没有使用static
关键字。因此,使用new
操作符创建类的新对象也会被当作对类的静态成员的引用
Java程序在它开始运行之前并非被完全加载,其各个部分是在必需时才加载的。class Gum {
static {
System. out. println ( "Loading Gum" ) ;
}
}
class Candy {
static {
System. out. println ( "Loading Candy" ) ;
}
}
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 creating Candy" ) ;
try {
Class. forName ( "chapter14.Gum" ) ;
} catch ( ClassNotFoundException e) {
e. printStackTrace ( ) ;
}
System. out. println ( "After Class.forName(\"Gum\")" ) ;
new Cookie ( ) ;
System. out. println ( "After creating Cookie" ) ;
}
}
forName()
是取得Class
对象的引用的一种方法。它是用一个包含目标类的文本名(注意拼写和大小写)的String
作输入参数,返回的是一个Class
对象的引用。如果Class.forName()
找不到要加载的类,它会抛出异常ClassNotFoundException
如果已经有一个很感兴趣的类型的对象时,可以通过调用getClass()
方法来获取Class
引用
getName()
获取全限定的类名,getSimpleName()
获取不包含包名的类名,getCanonicalName()
获取全限定的类名,isInterface()
方法得知是否为接口
getInterfaces()
返回的是Class
对象,它们表示在感兴趣的Class
对象中所包含的接口
getSuperClass()
方法查询其直接基类
newInstance()
方法是实现“虚拟构造器”的一种途径,虚拟构造器允许你声明:我不知道你的确切类型,但无论如何要正确地创建你自己interface HasBatteries {
}
interface Waterproof {
}
interface Shoots {
}
class Toy {
public Toy ( ) {
}
public Toy ( int i) {
}
}
class FancyToy extends Toy implements HasBatteries , Waterproof, Shoots {
public FancyToy ( ) {
super ( 1 ) ;
}
}
public class ToyTest {
static void printInfo ( Class< ? > cc) {
System. out. println ( "Class name: " + cc. getName ( ) ) ;
System. out. println ( "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 ( "chapter14.FancyToy" ) ;
} catch ( ClassNotFoundException e) {
e. printStackTrace ( ) ;
System. exit ( 1 ) ;
}
printInfo ( c) ;
for ( Class< ? > face : c. getInterfaces ( ) ) {
printInfo ( face) ;
}
Class< ? > up = c. getSuperclass ( ) ;
Object obj = null;
try {
obj = up. getDeclaredConstructor ( ) . newInstance ( ) ;
} catch ( InstantiationException e) {
System. out. println ( "Cannot instantiate" ) ;
System. exit ( 1 ) ;
} catch ( IllegalAccessException e) {
System. out. println ( "Cannot access" ) ;
System. exit ( 1 ) ;
} catch ( InvocationTargetException e) {
System. out. println ( "Cannot Invocation Target" ) ;
System. exit ( 1 ) ;
} catch ( NoSuchMethodException e) {
System. out. println ( "No Such Method" ) ;
System. exit ( 1 ) ;
}
printInfo ( obj. getClass ( ) ) ;
}
}
类字面常量 另一种获取Class
对象的引用Class< Boolean> type = Boolean. TYPE;
Class< Boolean> booleanClass = boolean . class ;
类字面常量不仅可以应用于普通的类,也可以应用于接口、数组以及基本数据类型。另外,对于基本数据类型的包装器类,还有一个标准字段TYPE
。TYPE
字段是一个引用,指向对应的基本数据类型的Class
对象。但建议使用.class
形式
当使用.class
来创建对Class
对象的引用时,不会自动地初始化该Class
对象。初始化被延迟到了对静态方法(构造器隐式地是静态的)或者非常数静态域进行首次引用时才执行class Initable {
static final int staticFinal = 47 ;
static final int staticFinal2 = ClassInitialization. rand. nextInt ( ) ;
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 ClassNotFoundException {
Class< Initable> 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) ;
System. out. println ( "After creating Initable3 ref" ) ;
Class< ? > initable3 = Class. forName ( "chapter14.Initable3" ) ;
}
}
从Initable
引用的创建中可以看到,仅使用.class
语法来获得对类的引用不会引发初始化。 但是,Class.forName()
立即就进行了初始化,就像Initable3
引用的创建中所看到的 static final
是一个编译期常量,如Initable.staticFinal
所看到的,那么这个值不需要对Initable
类进行初始化就可以被读取。 如果一个static
域不是final
的,那么在对它访问时,总是要求在它被读取之前,要先进行链接(为这个域分配内存空间)和初始化(初始化该存储空间),就像在对Initable2.staticNonFinal
的访问中所看到的那样。
为了使用类而做的准备工作实际包含三个步骤: – 加载:由类加载器执行,该步骤将查找字节码,并从这些字节码中创建一个Class
对象 – 链接:将验证类中的字节码,为静态域分配存储空间,并且如果需要的话,解析这个类创建的对其他类的所有引用 – 初始化:如果该类具有超类,则对其初始化,执行静态初始化器和静态初始化块
泛化的Class
引用 泛型类引用只能赋值为指向其声明的类型,但是普通的类引用可以被重新赋值为指向任何其他的Class
对象。通过使用泛型语法,可以让编译器强制执行额外的类型检查public class GenericClassReferences {
public static void main ( String[ ] args) {
Class intClass = int . class ;
Class< Integer> genericIntClass = int . class ;
genericIntClass = Integer. class ;
intClass = double . class ;
}
}
为了在使用泛化的Class
引用时放松限制,可以使用通配符?
,表示任何事物。Class<?>
优于平凡的Class
,平凡的Class
不会产生编译器警告信息。Class<?>
的好处是它表示你并非碰巧或者由于疏忽,而是用了一个非具体的类引用,你就是选择了非具体的版本。(提醒作用)Class< ? > clazz = int . class ;
clazz = double . class ;
限定一个Class
对象,为某个类型或者某个类型的子类型,需要将通配符和extends
关键字结合,创建一个范围。Class< ? extends List > listClass = ArrayList. class ;
List list = listClass. getDeclaredConstructor ( ) . newInstance ( ) ;
类的超类的Class
对象Class< FancyToy> ftClass = FancyToy. class ;
Class< ? super FancyToy> up = ftClass. getSuperclass ( ) ;
Object o = up. getDeclaredConstructor ( ) . newInstance ( ) ;
新的转型语法 Class
还添加了转型语法,cast()
方法。新的转型语法对于无法使用普通转型的情况显得非常有用(比如Class
对象不确定?)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;
}
}
类型转换前先做检查
关键字instanceof
,判断对象是不是某个特定类型的实例。
进行向下转型前,如果没有其他信息可以告诉你这个对象是什么类型,那么instanceof
是非常重要的,否则会得到一个ClassCastException
异常class Creature {
}
class Dog extends Creature {
public void bark ( ) {
System. out. println ( "汪汪汪" ) ;
}
}
public class instanceofTest {
public static void main ( String[ ] args) {
Creature dog = new Dog ( ) ;
if ( dog instanceof Dog ) {
( ( Dog) dog) . bark ( ) ;
}
}
}
动态的instanceof
Class.isInstance()
方法提供了一种动态地测试对象的途径boolean instance = List. class . isInstance ( new ArrayList ( ) ) ;
System. out. println ( instance) ;
递归计数 isAssignableFrom()
来确认是否为指定基类的继承类Class< List> listClass = List. class ;
System. out. println ( listClass. isAssignableFrom ( List. class ) ) ;
System. out. println ( listClass. isAssignableFrom ( ArrayList. class ) ) ;
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 ( ! baseType. isAssignableFrom ( type) ) {
throw new RuntimeException ( obj + " incorrect type: " + type + ", should be type or subtype of" ) ;
}
countClass ( type) ;
}
private void countClass ( Class< ? > type) {
Integer quantity = get ( type) ;
put ( type, quantity == null ? 1 : quantity + 1 ) ;
Class< ? > superclass = type. getSuperclass ( ) ;
if ( superclass != null && baseType. isAssignableFrom ( type) ) {
countClass ( superclass) ;
}
}
@Override
public String toString ( ) {
StringBuilder result = new StringBuilder ( "{" ) ;
for ( Map. Entry< Class< ? > , Integer> entry : entrySet ( ) ) {
result. append ( entry. getKey ( ) ) . append ( "=" ) . append ( entry. getValue ( ) ) . append ( ", " ) ;
}
result. delete ( result. length ( ) - 2 , result. length ( ) ) ;
result. append ( "}" ) ;
return result. toString ( ) ;
}
public static void main ( String[ ] args) {
TypeCounter counter = new TypeCounter ( Object. class ) ;
counter. count ( new ArrayList ( ) ) ;
counter. count ( new LinkedList ( ) ) ;
System. out. println ( counter) ;
}
}
注册工厂
反射
动态代理
代理是基本的设计模式之一,它是为了提供额外的或不同的操作,而插入的用来代替“实际”对象的对象。
如果想要将额外的操作从实际对象中分离到不同的地方,特别是希望能够很容易地做出修改,从没有使用额外的操作转为使用这些操作,或者反过来时,代理就显得很有用。interface Interface {
void doSomething ( ) ;
void SomethingElse ( String arg) ;
}
class RealObject implements Interface {
@Override
public void doSomething ( ) {
System. out. println ( "doSomething" ) ;
}
@Override
public void SomethingElse ( String arg) {
System. out. println ( "SomethingElse " + arg) ;
}
}
class SimpleProxy implements Interface {
private Interface proxied ;
public SimpleProxy ( Interface proxied ) {
this . proxied = proxied;
}
@Override
public void doSomething ( ) {
System. out. println ( "SimpleProxy doSomething " ) ;
proxied. doSomething ( ) ;
}
@Override
public void SomethingElse ( String arg) {
System. out. println ( "SimpleProxy SomethingElse " + arg) ;
proxied. SomethingElse ( arg) ;
}
}
public 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的动态代理比代理的思想更向前迈进了一步,因为它可以动态地创建代理并动态地处理对所代理方法的调用
在动态代理上所做的所有调用都会被重定向到单一的调用处理器上,它的工作是揭示调用的类型并确定相应的对策
通过调用静态方法Proxy.newProxyInstance()
可以创建动态代理,这个方法需要得到一个类加载器(你通常可以从已经被加载的对象中获取其类加载器,然后传递它),一个你希望该代理实现的接口列表(不是类或抽象类),以及InvocationHandler
接口的一个实现。
class DynamicProxyHandler implements InvocationHandler {
private Object proxied;
public DynamicProxyHandler ( Object proxied) {
this . proxied = proxied;
}
@Override
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) ;
}
}
public 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) ;
Interface proxy = ( Interface) Proxy. newProxyInstance ( Interface. class . getClassLoader ( ) , new Class [ ] {
Interface. class } , new DynamicProxyHandler ( real) ) ;
consumer ( proxy) ;
}
}
通过传递某些参数,过滤某些方法,只代理某些方法 @Override
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 ( proxied, args) ;
}
空对象
它可以接受传递给它的所代表的对象的消息,但是将返回表示为实际上并不存在任何“真实”对象的值。通过这种方式,你可以假设所有的对象都是有效的,而不必浪费编程精力去检查null
标记接口public interface Null {
}
创建bean
时,内部添加空对象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;
}
@Override
public String toString ( ) {
return "Person{" +
"first='" + first + '\'' +
", last='" + last + '\'' +
", address='" + address + '\'' +
'}' ;
}
public static class NullPerson extends Person implements Null {
public NullPerson ( ) {
super ( "None" , "None" , "None" ) ;
}
@Override
public String toString ( ) {
return "NullPerson" ;
}
}
public static final Person NULL = new NullPerson ( ) ;
}
使用bean
的类public class Position {
private String title;
private Person person;
public Position ( String title, Person person) {
this . title = title;
if ( person == null)
person = Person. NULL;
this . person = person;
}
public Position ( String title) {
this . title = title;
person = Person. NULL;
}
public String getTitle ( ) {
return title;
}
public void setTitle ( String title) {
this . title = title;
}
public Person getPerson ( ) {
return person;
}
public void setPerson ( Person person) {
if ( person == null)
person = Person. NULL;
this . person = person;
}
@Override
public String toString ( ) {
return "Position{" +
"title='" + title + '\'' +
", person=" + person +
'}' ;
}
}
通过动态代理实现空对象
接口与类型信息
转载自 blog.csdn.net/weixin_42524843/article/details/113928582