zoukankan      html  css  js  c++  java
  • 类型信息(编程思想)

    类型信息
    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,太恶心了。有时候引入空对象的思想会很有用

  • 相关阅读:
    .net core 2.2, new Bitmap出错 The type initializer for 'Gdip' threw an exception
    瑞萨单片机学习笔记(待续)
    Linux-GitLab安装及汉化
    mysql5.7 ibtmp1文件过大
    #和$的区别
    RTP封装h264
    一个项目同时需要向两个地址推送
    git命令
    echart绘制进度条、仪表盘、各种样式的折线图、饼图、环形图、地图等
    前端在实现类似控制台命令行或者告警信息提示时,需要保持滚动条始终停留在最新的信息位置,也就是最底部
  • 原文地址:https://www.cnblogs.com/aigeileshei/p/7459429.html
Copyright © 2011-2022 走看看