java编程思想第四版第十四章 类型信息总结

简介: 所有的类都是在对其第一次使用的时候,动态加载到JVM中的。当程序创建第一个对类的静态成员的引用时,就会加载这个类。这说明构造器也是类的静态方法。即使在构造器之前并没有static关键字,这个类也会被加载。

1. Class 对象:


  • 所有的类都是在对其第一次使用的时候,动态加载到JVM中的。当程序创建第一个对类的静态成员的引用时,就会加载这个类。这说明构造器也是类的静态方法。即使在构造器之前并没有static关键字,这个类也会被加载。


  • java程序在它开始运行之前并非完全被加载。其各个部分是在必要时才加载的。


  • 类加载器的执行过程



    1、 类加载器首先检查这个类的Class对象是否已经加载。如果尚未加载,默认的类加载器就会根据类名查找.class文件。


    2、在这个类的字节码被加载是,他们会接收验证,以确保其没有被损坏。并且不包含不良代码。这时java中用于安全防范目的的措施之一。


    3、一旦某个类被加载,他就用来创建这个类的所有对象。


下面这个例子说明了一下两点:


1. 类何时被加载


2. 如何加载


package net.mindview.typeinfo;
/**
 * 首先, 下面的每一个类都有一个静态的代码块. 
 * 这个代码块, 在类第一次被加载时执行。
 * @author samsung
 *
 */
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. 以后都是从这个类中创建实例对象
        new Candy();
        System.out.println("After creating Candy!");
        try {
            //Class.forName 获取对象引用的一种方法.参数是类的全限定文件名. 返回值是一个Class对象的引用.
            //如果Gum没有被加载, 那么这个类就会被加载.
            //使用类加载器加载类Gum, 在第一次加载Gum时, 也会主动去加载Gum这个类, 以后就从这个类中创建实例.
            Class.forName("Gum");
        } catch (ClassNotFoundException e) {
            System.out.println("Could`t find Gum");
        }
        System.out.println("After Class.forName(\"Gum\")");
        new Cookie();
        System.out.println("After creating Cookie");
        //这个例子展示, 第二次实例化Cookie是, static静态代码块没有被再次执行, 它只会在第一次加载时执行.
        new Cookie();
        System.out.println("After creating Cookie twice");
    }
}


下面展示如何通过Class来发现整个类的基本结构. 详细看代码注释


package net.mindview.typeinfo.toys;
/**
 * 以下:展示了完全的获取一个类的完整的继承结构.
 * @author samsung
 *
 */
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){
        /**
         * 这里调用了Class的一些基本方法
         * cc.isInterface(): 是接口么
         * cc.getSimpleName(): 类名, 不含路径
         * cc.getCanonicalName(): 全限定类名
         */
        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("net.mindview.typeinfo.toys.FancyToy");
        } catch (ClassNotFoundException e) {
            System.out.println("Can`t find FancyToy");
            System.exit(1);
        }
        printInfo(c);
        System.out.println();
        /**
         * c.getInterfaces(): 获取这个类已继承的所有的接口
         */
        for(Class face: c.getInterfaces()){
            //打印接口类
            printInfo(face);
            System.out.println();
        }
        /**
         * c.getSuperclass(): 获取这个类的父类
         */
        Class up = c.getSuperclass();
        Object obj = null;
        try {
            //将父类实例化
            //使用newInstance来实例化的类不许带有一个默认的构造器
            obj = up.newInstance();
        } catch (InstantiationException e) {
            System.out.println("Can`t instantiate");
            System.exit(1);
        } catch (IllegalAccessException e) {
            System.out.println("Can`t instantiate");
            System.exit(1);
        }
        //打印父类基本细腻
        printInfo(obj.getClass());
    }
}


运行结果:


Class name:net.mindview.typeinfo.toys.FancyToy is interface ? [false]
Simple name: FancyToy
Canonical name: net.mindview.typeinfo.toys.FancyToy
Class name:net.mindview.typeinfo.toys.HasBatteries is interface ? [true]
Simple name: HasBatteries
Canonical name: net.mindview.typeinfo.toys.HasBatteries
Class name:net.mindview.typeinfo.toys.Waterproof is interface ? [true]
Simple name: Waterproof
Canonical name: net.mindview.typeinfo.toys.Waterproof
Class name:net.mindview.typeinfo.toys.Shoots is interface ? [true]
Simple name: Shoots
Canonical name: net.mindview.typeinfo.toys.Shoots
Class name:net.mindview.typeinfo.toys.Toy is interface ? [false]
Simple name: Toy
Canonical name: net.mindview.typeinfo.toys.Toy


  • 总结:


  • cc.isInterface(): 是接口么


  • cc.getSimpleName(): 类名, 不含路径


  • cc.getCanonicalName(): 全限定类名


  • c.getInterfaces(): 获取这个类已继承的所有的接口


  • c.getSuperclass(): 获取这个类的父类


  • 使用newInstance来实例化的类不许带有一个默认的构造器


2. 类字面常量


  Toy.class


  • 所有的类都有类字面常量,普通类, 接口,数组,以及基本数据类型。


  • 使用 .class创建一个Class对象的引用时,不会自动初始化该Class对象,这个加载过程包括以下三步


        a.加载。 由类加载器执行。该步骤将查找字节码,通常在classpath所制定的路径中查找,并从这些字节码中创建一个class对象。


        b.链接:验证类中的字节码,为静态域分配存储空间。并且,如果必要的话,会解析这个类创建的对其他类的所有引用。


        c.初始化:如果该类具有超类,则对其初始化。执行静态初始化构造器或者静态初始化代码块。。初始化的过程将会被延迟,直到对静态方法或者非常数静态域进行首次引用时才执行初始化。(注意:类构造器其实就是隐式的静态方法)


package net.mindview.typeinfo;
import java.util.Random;
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 ClassNotFoundException {
        //下面这句话执行完,并没有对Initable这个类进行初始化.
        Class initable = Initable.class;
        System.out.println("创建Initable引用之后");
        //没有触发初始化---Initable.staticFinal是一个常数, 所以不会触发初始化
        System.out.println(Initable.staticFinal);
        System.out.println("-------1----------");
        //触发初始化 -- Initable.staticFinal2是引用常数, 触发初始化
        System.out.println(Initable.staticFinal2);
        System.out.println("---------2--------");
        //触发初始化---Initable2.staticNonFinal非常数静态域, 调用后会触发初始化
        System.out.println(Initable2.staticNonFinal);
        System.out.println("---------3--------");
        //执行Class.forName的时候, 会将对象初始化.
        Class initable3 = Class.forName("net.mindview.typeinfo.Initable3");
        System.out.println("创建Initable3引用后");
        System.out.println("---------4--------");
        System.out.println(Initable3.staticNonFinal);
    }
}


结果运行:


创建Initable引用之后
47
-------1----------
Initializing Initable
258
---------2--------
Initializing Initable2
147
---------3--------
Initializing Initable3
创建Initable3引用后
---------4--------
74


  1. 详细看这个demo. 里面主要说了几点


  • .class方式构造的对象的实例化会延迟


  • 静态常量的调用不会触发实例化


  • 非静态常量的调用会触发实例化.


  • class.forName()会立即触发初始化  


3.  泛化的class引用


  • 我们使用通配符堆Class类进行泛型化, 通配符是 ? ,表示“任何事物”,例如:


package net.mindview.typeinfo;
public class WildcardClassReferences {
    public static void main(String[] args) {
        /**
         * Class<?> 使用的时通配符来表示泛型.
         */
        Class<?> clazz = int.class;
        clazz = double.class;
    }
}


创建限定某种类型的Class, 可以使用?extends Number形式。


package net.mindview.typeinfo;
public class BoundedClassReferences {
    public static void main(String[] args) {
        Class<? extends Number> clazz = int.class;
        clazz = double.class;
        //下面这句话就是报编译异常, 因为String 不是Number的一类
        //clazz = String.class;
    }
}


使用泛型类存储一个类。如下例:


package net.mindview.typeinfo;
import java.util.ArrayList;
import java.util.List;
/**
 * 定义一个IntegerCounted类--Integer计数器
 */
class CountedInteger {
    //计数器总数
    private static long counter;
    //每一个计数类的编号
    private final long id = counter ++;
    //打印计数器编号
    public String toString(){return Long.toString(id);};
}
public class FilledList<T> {
    //这里定义一个类型, 表示该类是处理拿一个特定类型的, 在实例化的时候, 传递实际类型
    Class<T> type;
    public FilledList(Class<T> type){
        this.type = type;
    }
    public List<T> create(int number) throws InstantiationException, IllegalAccessException{
        List<T> list = new ArrayList<T>();
        for(int i=0; i<number; i++){
            list.add(type.newInstance());
        }
        return list;
    }
    public static void main(String[] args) {
        try {
            FilledList<CountedInteger> fl = new FilledList(CountedInteger.class);
            List<CountedInteger> list = fl.create(5);
            //打印list集合,就是调用list集合中每个元素的toString方法
            System.out.println(list);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}


如果你知道某个具体类, 想获得这个具体类的超类, 使用“”?super 具体类“的方式实现,如下例:


package net.mindview.typeinfo.toys;
/**
 * 以下:展示了完全的获取一个类的完整的继承结构.
 * @author samsung
 *
 */
interface HasBatteries {}
interface Waterproof {}
interface Shoots {}
interface other {}
class Toy{
    Toy(){}
    Toy(int i){}
}
class FancyToy extends Toy implements HasBatteries, Waterproof, Shoots, other{
    FancyToy() {super(1);}
}
public class ToyTest {
    static void printInfo(Class cc){
        /**
         * 这里调用了Class的一些基本方法
         * cc.isInterface(): 是接口么
         * cc.getSimpleName(): 类名, 不含路径
         * cc.getCanonicalName(): 全限定类名
         */
        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("net.mindview.typeinfo.toys.FancyToy");
        } catch (ClassNotFoundException e) {
            System.out.println("Can`t find FancyToy");
            System.exit(1);
        }
        printInfo(c);
        System.out.println();
        /**
         * c.getInterfaces(): 获取这个类已继承的所有的接口
         */
        for(Class face: c.getInterfaces()){
            //打印接口类
            printInfo(face);
            System.out.println();
        }
        /**
         * c.getSuperclass(): 获取这个类的父类
         */
        Class up = c.getSuperclass();
        Object obj = null;
        try {
            //将父类实例化
            //使用newInstance来实例化的类不许带有一个默认的构造器
            obj = up.newInstance();
        } catch (InstantiationException e) {
            System.out.println("Can`t instantiate");
            System.exit(1);
        } catch (IllegalAccessException e) {
            System.out.println("Can`t instantiate");
            System.exit(1);
        }
        //打印父类基本细腻
        printInfo(obj.getClass());
    }
}


package net.mindview.typeinfo.toys;
public class GenericToyTest {
    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        Class<FancyToy> ftClass = FancyToy.class;
        FancyToy fancyToy = ftClass.newInstance();
        //如果你想得到某个实现类的超类, 那么他的Class应该如何写呢?
        Class<? super FancyToy> up = ftClass.getSuperclass();
        //Class<Toy> up1 = ftClass.getSuperclass(); 
        Object obj = up.newInstance();
    }
    /**
     * 这里FancyToy的超类是Toy. 而我想得到Toy这个超类, 并不能直接这样写
     * Class<Toy> up = ftClass.getSuperclass(); 
     * 这样编译不通过.
     */
}


使用instanceof判断类型. 最好在进行向下转型时, 都是用instanceof进行判断


package net.mindview.typeinfo;
import net.mindview.initialization.Dog;
/**
 * 类型判断
 */
public class InstanceofTest {
    public static void main(String[] args) {
        //这里就是对x的类型进行的判断
        if("x" instanceof String){
            //执行String类型的方法
        }
    }
}


4. 反射


  • getMethods()方法,获取的时整个继承树中的全部方法


  • getConstructors()方法,获取的是所有的构造器


  • 可以通过解析对象所代表的方法, 并获取其名字, 返回值, 参数等信息.


  • 也可以使用toString()方法, 返回的是包含有完整方法特征信息的字符串. 包括 返回值,返回类型,参数等.


package net.mindview.typeinfo;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.regex.Pattern;
public class ShowMethods {
    private static String usage = ""
            + "usage:\n"
            + "ShowMethods qualified.class.name\n"
            + "To show all methods in class or:\n"
            + "ShowMethods qualified.class.name. word\n"
            + "To search for methodds invoiving 'word'";
    private static Pattern p = Pattern.compile("\\w+\\.");
    public static void main(String[] args) {
        if(args.length<1){
            System.out.println(usage);
            System.exit(1);
        }
        int lines = 0; 
        try {
            Class<?> c = Class.forName(args[0]);
            //getMethods获取的是整个继承树中所有的方法
            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) {
            e.printStackTrace();
        }
    }
}


输入参数


net.mindview.typeinfo.ShowMethods


运行结果:


public static void main(String[])
public final void wait(long,int) throws InterruptedException
public final native void wait(long) throws InterruptedException
public final void wait() throws InterruptedException
public boolean equals(Object)
public String toString()
public native int hashCode()
public final native Class getClass()
public final native void notify()
public final native void notifyAll()
public ShowMethods()


输入参数


net.mindview.typeinfo.ShowMethods
net.mindview.typeinfo.ShowMethods


运行结果


public static void main(String[])
public ShowMethods()


相关文章
|
2月前
|
安全 Java 编译器
Java类型提升与类型转换详解
本文详解Java中的类型提升与类型转换机制,涵盖类型提升规则、自动类型转换(隐式转换)和强制类型转换(显式转换)的使用场景与注意事项。内容包括类型提升在表达式运算中的作用、自动转换的类型兼容性规则,以及强制转换可能引发的数据丢失和运行时错误。同时提供多个代码示例,帮助理解byte、short、char等类型在运算时的自动提升行为,以及浮点数和整型之间的转换技巧。最后总结了类型转换的最佳实践,如避免不必要的转换、使用显式转换提高可读性、金融计算中使用BigDecimal等,帮助开发者写出更安全、高效的Java代码。
152 0
|
2月前
|
安全 IDE Java
Java记录类型(Record):简化数据载体类
Java记录类型(Record):简化数据载体类
316 120
|
2月前
|
Java 测试技术
Java浮点类型详解:使用与区别
Java中的浮点类型主要包括float和double,它们在内存占用、精度范围和使用场景上有显著差异。float占用4字节,提供约6-7位有效数字;double占用8字节,提供约15-16位有效数字。float适合内存敏感或精度要求不高的场景,而double精度更高,是Java默认的浮点类型,推荐在大多数情况下使用。两者都存在精度限制,不能用于需要精确计算的金融领域。比较浮点数时应使用误差范围或BigDecimal类。科学计算和工程计算通常使用double,而金融计算应使用BigDecimal。
1122 102
|
9天前
|
存储 算法 安全
Java集合框架:理解类型多样性与限制
总之,在 Java 题材中正确地应对多样化与约束条件要求开发人员深入理解面向对象原则、范式编程思想以及JVM工作机理等核心知识点。通过精心设计与周密规划能够有效地利用 Java 高级特征打造出既健壮又灵活易维护系统软件产品。
38 7
|
21天前
|
Java 开发者
Java 函数式编程全解析:静态方法引用、实例方法引用、特定类型方法引用与构造器引用实战教程
本文介绍Java 8函数式编程中的四种方法引用:静态、实例、特定类型及构造器引用,通过简洁示例演示其用法,帮助开发者提升代码可读性与简洁性。
|
2月前
|
安全 算法 Java
Java泛型编程:类型安全与擦除机制
Java泛型详解:从基础语法到类型擦除机制,深入解析通配符与PECS原则,探讨运行时类型获取技巧及最佳实践,助你掌握泛型精髓,写出更安全、灵活的代码。
|
11月前
|
存储 Java 开发者
Java 中 Set 类型的使用方法
【10月更文挑战第30天】Java中的`Set`类型提供了丰富的操作方法来处理不重复的元素集合,开发者可以根据具体的需求选择合适的`Set`实现类,并灵活运用各种方法来实现对集合的操作和处理。
639 113
|
7月前
|
存储 传感器 缓存
java变量与数据类型:整型、浮点型与字符类型
### Java数据类型全景表简介 本文详细介绍了Java的基本数据类型和引用数据类型,涵盖每种类型的存储空间、默认值、取值范围及使用场景。特别强调了`byte`、`int`、`long`、`float`、`double`等基本类型在不同应用场景中的选择与优化,如文件流处理、金融计算等。引用数据类型部分则解析了`String`、数组、类对象、接口和枚举的内存分配机制。
205 15
|
7月前
|
Java
课时11:Java数据类型划分(浮点类型)
课时11介绍了Java中的浮点数据类型。主要内容包括:1. 定义小数,默认使用Double类型;2. 定义Float变量,需在数值后加&quot;F&quot;或&quot;f&quot;进行强制转换;3. 观察不同类型计算结果,如Int型除法会丢失精度,需至少包含一个Double或Float类型以确保准确性。总结指出,在复杂计算中推荐使用Double类型以避免精度损失。
161 5
|
6月前
|
Java
java中一个接口A,以及一个实现它的类B,一个A类型的引用对象作为一个方法的参数,这个参数的类型可以是B的类型吗?
本文探讨了面向对象编程中接口与实现类的关系,以及里氏替换原则(LSP)的应用。通过示例代码展示了如何利用多态性将实现类的对象传递给接口类型的参数,满足LSP的要求。LSP确保子类能无缝替换父类或接口,不改变程序行为。接口定义了行为规范,实现类遵循此规范,从而保证了多态性和代码的可维护性。总结来说,接口与实现类的关系天然符合LSP,体现了多态性的核心思想。
135 0

热门文章

最新文章