类型信息
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 creating Candy");
try{
Class.forName("Gum");
}catch(Exception ex){
System.out.println("Couldn't find Gum");
}
System.out.println("After Class.forName("Gum")");
new Cookie();
System.out.println("After creating Cookie");
}
/**
* 每个类中的static块在第一次加载时执行
* Class对象仅在需要的时候被加载,static初始化是在类加载时进行的
* Class.forName()得到class对象的引用,如果该类没有进行加载,那就加载它。终于知道加载jdbc连接驱动的时候真正想要执行的是static块。Class.forName("com.jdbc.mySql.Driver")
*/
}
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() + " 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("com.zhen.type_information.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());
}
}
Class.forName()中的字符串中,必须使用全限定定名(包含包名)
getSimpleName()和getCnnonicalName()来产生不含包名的类名和全限定的类名
isInterface()
Class.getInstance()返回的是Class对应对象
getSuperclass()查询直接基类
类字面常量
java还提供了另一种方法来生成对Class对象的引用,即使用类字面常量。类似这样:
FancyToy.class
这样做不仅更简单,而且更安全,因为它在编译时就会受到检查(因此不需要置于try语句块中)。并且它根除了对forName()方法的调用,所以也更高效。
例如:
boolean.class 等价于Boolean.TYPE
void.class 等价于Void.TYPE
建议使用".class",以保证与普通类的一致性。
注意,当使用“.class”来创建对Class对象的引用时,不会自动地初始化该Class对象。为了使用类而做的准备工作实际包含三个步骤:
1、加载,这是由类加载器执行。该步骤将查找字节码,并从这些字节码中创建一个Class对象
2、链接。在链接阶段将验证类中的字节码,为静态域分配存储空间,而且如果必须的话,将解析这个类创建的对其他类的所有引用
3、初始化。如果该类具有超类,将对其初始化,执行静态初始化器和静态初始化块
初始化被延迟到了对静态方法(构造器隐式地是静态的)或者非常数静态域进行首次引用时才执行
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("com.zhen.type_information.Initable3");
System.out.println("After creating Initable3 ref");
System.out.println(Initable3.staticNonFinal);
}
}
可以看出,使用方法.class语法来获得对类的引用不会引发初始化。但是为了产生Class引用,Class.forName()就立即进行了初始化。
如果一个static final的值是“编译期常量”,就像Initable.staticFinal那样,那么这个值不需要对Initable类进行初始化就可以读取
泛化的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
}
}
普通的类引用不会产生警告信息,你可以看到,尽管泛型类引用只能赋值为指向其声明的类型,但是普通的类引用可以被重新赋值为指向任何其他的Class对象。通过泛型语法,可以让编译器强制执行额外的类型检查。
如果你希望稍微放松一些这种限制,应该怎么办呢?咋一看,好像你应该能够执行类似下面的操作:
Class<Number> genericNumberClass = int.class;
看起来似乎是起作用的,因为Integer继承自Number。但是它无法工作,因为Integer Class对象不是Number对象的子类。
为了在使用泛化的Class引用时放松限制,我们使用了通配符,它是Java泛型中的一部分。通配符就是“?”,表示“任何事物”。
public class WildcardClassReferences {
public static void main(String[] args) {
Class<?> intClass = int.class;
intClass = double.class;
}
}
在JavaSE5中,Class<?>优于平凡的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 anyting else derived from Number
// bounded = String.class; Illegal
}
}
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));
}
}
public class GenericToyTest {
public static void main(String[] args) throws Exception {
Class<FancyToy> ftClass = FancyToy.class;
//Produces exact type
FancyToy fancyToy = ftClass.newInstance();
Class<? super FancyToy> up = ftClass.getSuperclass();
// Class<Toy> up2 = ftClass.getSuperclass(); this won't compile
Object obj = up.newInstance();
}
}
如果你手头的是超类,那编译器将只允许你声明超类引用是“某个类”,它是FancToy超类,就像在表达式Class<? Super FancyToy>中所看到的,而不会接受Class<Toy>这样的声明。这看起来显得有些怪,因为getSuperClass()方法返回的是基类(不是接口),并且编译器在编译期就知道它是什么类型了——在本类中就是Toy.class——而不仅仅只是“某个类,它是FancyToy超类”。不管这样,正是由于这种含糊性,up.newInstance()返回的不是精确类型,而只是Object。
新的类型转换
java SE5还添加了用于Class引用的转型语法,即cast()方法 ,说实话,这让我想起异常ClassCastException
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
}
}
类型转换前先做检查
传统的类型转换,执行错误报错ClassCastException
通过查询Class对象可以获取运行时所需的信息
java中第三种形式,关键字instanceof。返回布尔值,告诉我们对象是不是某个特定类型的实现
动态的instanceof
Class对象的isInstance()方法
反射:运行时的类信息
Class类与java.lang.reflect类库一起对反射的概念进行了支持,该类库包括了Field、Method以及Constructor类
类方法提取器:
public class ShowMethods {
public static String usage =
"usage:
" +
"ShowMethods qualified.class.name
" +
"To show all methods in class or:
" +
"ShowMethods qualified.class.name word
" +
"To search for methods involving 'word'";
private static Pattern p = Pattern.compile("\w+\.");
public static void main(String[] args) {
args = new String[]{"com.zhen.type_information.t1.ClassCasts"};
if(args.length < 1) {
System.out.println(usage);
System.exit(0);
}
int lines = 0;
try{
Class<?> c = Class.forName(args[0]);
Method[] methods = c.getMethods();
Constructor[] ctors = c.getConstructors();
if(args.length == 1){
for(Method method : methods) {
System.out.println(p.matcher(method.toString()).replaceAll(""));
}
for(Constructor ctor : ctors){
System.out.println(p.matcher(ctor.toString()).replaceAll(""));
}
lines = methods.length + ctors.length;
} else {
for(Method method : methods){
if(method.toString().indexOf(args[1]) != -1) {
System.out.println(p.matcher(method.toString()).replaceAll(""));
}
lines ++;
}
for(Constructor ctor : ctors){
if(ctor.toString().indexOf(args[1]) != -1){
System.out.println(p.matcher(ctor.toString()).replaceAll(""));
}
lines++;
}
}
}catch (ClassNotFoundException e){
System.out.println("No such class: " + e);
}
}
}
动态代理
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);
}
}
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);
//Insert a proxy and call again
Interface proxy = (Interface) Proxy.newProxyInstance(
Interface.class.getClassLoader(),
new Class[]{ Interface.class},
new DynamicProxyHandler(real)
);
consumer(proxy);
}
}
Proxy.newProxyInstance()可以创建动态代理,这个方法需要得到一个类加载器,一个你希望该代理实现的接口列表(不是类或抽象类),以及InvocationHandler接口的一个实现。动态代理可以将所有调用重定向到调用处理器,因此通常会向调用处理器的构造器传递一个“实际”对象的引用,从而使得调用处理器在执行其中介任务时,可以将请求转发。
空对象Null
每次引用都测试是否为null,太恶心了。有时候引入空对象的思想会很有用