Java『基础知识』

2019-04-04  本文已影响0人  cname_1

JVM

JVM是运行Java字节码的虚拟机。JVM有针对不同系统的特定实现,目的在不同的系统平台上运行相同的字节码。.java文件经过JDK的javac编译为.class文件,.class文件又被JVM编译成机器可执行的二进制机器码。

我们需要格外注意的是 .class->机器码 这一步。在这一步 jvm 类加载器首先加载字节码文件,然后通过解释器逐行解释执行,这种方式的执行速度会相对比较慢。而且,有些方法和代码块是经常需要被调用的,也就是所谓的热点代码,所以后面引进了 JIT 编译器,JIT 属于运行时编译。当 JIT 编译器完成第一次编译后,其会将字节码对应的机器码保存下来,下次可以直接使用。而我们知道,机器码的运行效率肯定是高于 Java 解释器的。这也解释了我们 为什么经常会说 Java 是编译与解释共存的语言。

HotSpot采用了惰性评估(Lazy Evaluation)的做法,根据二八定律,消耗大部分系统资源的只有那一小部分的代码(热点代码),而这也就是JIT所需要编译的部分。JVM会根据代码每次被执行的情况收集信息并相应地做出一些优化,因此执行的次数越多,它的速度就越快。JDK 9引入了一种新的编译模式AOT(Ahead of Time Compilation),它是直接将字节码编译成机器码,这样就避免了JIT预热等各方面的开销。JDK支持分层编译和AOT协作使用。但是 ,AOT 编译器的编译质量是肯定比不上 JIT 编译器的。

重载与重写

重载:发生在同一类中,方法名相同,方法签名(参数类型、个数、顺序不同)可能不同;注意,返回类型和访问修饰符不算在方法签名中,不能作为重载的判断依据。

重写:发生在继承类中,方法签名需要相同,方访问权限子类中实现需要大于等于父类中实现。

构造器Constructor是否可以被override?

父类的构造方法和私有属性不能被继承,所以Constructor也不能被重写(override),但是可以被重载(overload)。

Java面向对象编程三大特性

封装、继承、多态,其中多态指定是程序的引用变量可以在运行时的向上转型动态确定。Java中可以通过继承接口实现多态。

String、StringBuilder、StringBuffer

可变性:首先String是用final修饰的保存字符的数组,因为字符串是不可变的,所以当直接定义字符串(如:"abc")时,会去常量池中寻找该变量值,存在直接返回使用,不存在再行创建;StringBuilder和StringBuffer都是继承自AbstractStringBuilder类,该类也是使用数组保存字符串,和String不同的是没有使用final修饰,所以是可变的。

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];
    ...
}

线程安全性:String是不可变的,所以线程安全;StringBuffer是线程安全的;StringBuilder是非线程安全的,所以性能比StringBuffer高一点。

==和equals()

默认情况下==用来比较两个对象的引用地址是否相同,或者比较两个常量(基础类型分布在常量池中)是否相等;equals用来比较对象的具体属性的值是否相等;有时我们只需要比较两个对象包含的属性值是否相等,这时可以重写equals方法来实现。

Object的hashCode()和HashMap中的hashCode()

在使用==判断两个变量是否相等,比较的是两个变量的内存地址,和Object的hashCode()并没有直接的关系;先看Object中的hashCode()

/**
     * Returns a hash code value for the object. This method is
     * supported for the benefit of hash tables such as those provided by
     * {@link java.util.HashMap}.
     * <p>
     * The general contract of {@code hashCode} is:
     * <ul>
     * <li>Whenever it is invoked on the same object more than once during
     *     an execution of a Java application, the {@code hashCode} method
     *     must consistently return the same integer, provided no information
     *     used in {@code equals} comparisons on the object is modified.
     *     This integer need not remain consistent from one execution of an
     *     application to another execution of the same application.
     * <li>If two objects are equal according to the {@code equals(Object)}
     *     method, then calling the {@code hashCode} method on each of
     *     the two objects must produce the same integer result.
     * <li>It is <em>not</em> required that if two objects are unequal
     *     according to the {@link java.lang.Object#equals(java.lang.Object)}
     *     method, then calling the {@code hashCode} method on each of the
     *     two objects must produce distinct integer results.  However, the
     *     programmer should be aware that producing distinct integer results
     *     for unequal objects may improve the performance of hash tables.
     * </ul>
     * <p>
     * As much as is reasonably practical, the hashCode method defined by
     * class {@code Object} does return distinct integers for distinct
     * objects. (This is typically implemented by converting the internal
     * address of the object into an integer, but this implementation
     * technique is not required by the
     * Java&trade; programming language.)
     *
     * @return  a hash code value for this object.
     * @see     java.lang.Object#equals(java.lang.Object)
     * @see     java.lang.System#identityHashCode
     */
    public native int hashCode();

首先看到这是一个native方法,方法注释

其次HashMap只是一种数据结构,其存在的理由是为了集成类似数组的快速查找,又想避免为了存储少数大范围值的数据而创建一个大数组的问题。通过hash函数可以把这些大范围值的数据映射到HashMap的数组中去,如果发生哈希碰撞,则会是使用链表,而在Java 8.0以后,使用红黑树替代了链表;

具体HashMap可参考:HashMap实现原理及源码分析

equals()和hashCode()

​ 以HashSet为例,当需要插入、查询一个对象时,先计算该对象的hashCode,看是否存在相同的hashCode,如果存在,再对比他们的equals方法是否相等,如果equals方法也相等,则表明是同一个对象,拒绝插入;如果equals方法不相等,则对对象的重新进行hash,插入到其他位置。

Java中的序列化和反序列化(Serializable)

Java序列化就是将一个对象转化为一个****二进制表示的字节数组,如果不想对某些字段进行序列化,可以使用transient关键字修饰;在反序列化时如果serialVersionUID被修改的话,反序列化会失败;当父类实现了Serializable接口的时候,所有的子类都能被序列化,当子类实现Serializable接口时,父类没有,则父类中的属性不能被序列化。

Collections类和Arrays工具类的常用方法

Collections类
Arrays类
    val iArray = intArrayOf(1, 3, 4, 5, 6, 7, 8, 8, 6, 5, 4)
        Arrays.sort(iArray)
        for (v in iArray) {
            print("$v ")
        }
    >>>输出:1 3 4 4 5 5 6 6 7 8 8
    val arrayA = intArrayOf(1,3,5)
    val arrayB = intArrayOf(1,3,5)
    println(Arrays.equals(arrayA,arrayB))
    >>>输出:true
    val names = Arrays.asList("Larry", "Moe", "Curly")
    println(names)
    >>>输出:[Larry, Moe, Curly]

Java基础关键字

final
static
为什么static方法中不能使用this和super?

static修饰静态方法是属于类的;而this指向当前对象,super代表父类对象引用;静态方法属于类,而this、super针对对象。同样的问题还有:为什么不能在静态方法内调用外部类的非静态成员?一样的答案。

上一篇下一篇

猜你喜欢

热点阅读