被你忽略掉的 Java 细节知识
Posted 吴豪杰
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了被你忽略掉的 Java 细节知识相关的知识,希望对你有一定的参考价值。
静态代码块、构造代码块、构造函数同时存在时的执行顺序:静态代码块 -> 构造代码块 -> 构造函数
为什么不支持多继承呢?因为当一个类同时继承两个父类时,两个父类中有相同的功能,那么子类对象调用该功能时,运行哪一个呢?
父类中通常是不会出现同名成员变量的,因为父类中只要定义了,子类就不用在定义了,直接继承过来用就可以了。
子类的所有构造函数中的第一行,其实都有一条隐身的语句super();
接口: 成员变量:public static final; 成员方法:public abstract; 注意 abstract 与 static 不能共存,所以接口中的方法不是静态的。
抽象类和接口的区别:
- 抽象类只能被继承,而且只能单继承,接口需要被实现,而且可以多实现
- 抽象类中可以定义非抽象方法,子类可以直接继承使用。接口都有抽象方法,需要子类去实现
- 抽象类使用的是
is-a
关系,接口使用的是like-a
关系 - 抽象类的成员修饰符可以自定义,接口中的成员修饰符是固定的
多态: 父类引用或者接口的引用指向了自己的子类对象
- 成员变量: 无论编译和运行,成员变量参考的都是引用变量所属的类中的成员变量。(成员变量 — 编译运行都看 = 左边 )
- 成员函数: 在子父类中,对于一模一样的成员函数,有一个特性:覆盖。(成员函数 — 编译看 = 左边,运行看 = 右边 )
- 静态函数: 静态方法,其实不所属于对象,而是所属于该方法所在的类。(静态函数 — 编译运行都看 = 左边 )
内部类
- 可以直接访问外部类中的成员。而外部类想要访问内部类,必须要建立内部类的对象
- 如果内部类被静态修饰,相当于外部类,会出现访问局限性,只能访问外部类中的静态成员
- 如果内部类中定义了静态成员,那么该内部类必须是静态的
- 当内部类被定义在局部位置上,只能访问局部中被
final
修饰的局部变量
异常体系
Throwable:可抛出的
|–Error:错误,一般情况下,不编写针对性的代码进行处理,通常是jvm
发生的,需要对程序进行修正
|–Exception:异常,可以有针对性的处理方式try对应多个catch时,如果有父类的catch语句块,一定要放在下面
异常分两种:
- 编译时被检查的异常,只要是Exception及其子类都是编译时被检测的异常
- 运行时异常,其中Exception有一个特殊的子类RuntimeException,以及RuntimeException的子类是运行异常,也就说这个异常是编译时不被检查的异常
System.exit(0); //退出jvm,只有这种情况finally不执行
Runnable 接口出现的原因:
- 因为实现Runnable接口可以避免单继承的局限性
- 将线程要执行的任务封装成了对象, 便于扩展
同步(synchronized):
- 实例方法使用的锁是
this
锁 - 类方法使用的
类字节码
锁
- 实例方法使用的锁是
同步是隐式的锁操作,而
Lock
对象是显示的锁操作,它的出现就替代了同步。Lock
接口中并没有直接操作等待唤醒的方法,而是将这些方式又单独封装到了一个对象中。这个对象就是Condition
,将Object
中的三个方法进行单独的封装。并提供了功能一致的方法await()
、signal()
、signalAll()
体现新版本对象的好处。StringBuffer 和 StringBuilder 的区别:
- StringBuffer线程安全, StringBuilder线程不安全
- 单线程操作,使用StringBuilder 效率高; 多线程操作,使用StringBuffer 安全
基本数据类型包装类中,只有
Character
类没有parse(...)
方法Map 集合对比:
- Hashtable:底层是哈希表数据结构,是线程同步的。不可以存储null键,null值
- HashMap:底层是哈希表数据结构,是线程不同步的。可以存储null键,null值。替代了Hashtable
- TreeMap:底层是二叉树结构,可以对map集合中的键进行指定顺序的排序,JDK 1.8 中源码表现为红黑树
Map 中要保证键的唯一性
InputStreamReader isr = new InputStreamReader(new FileInputStream(“a.txt”),”gbk”); 指定编码
递归: 当一个功能被重复使用,而每一次使用该功能时的参数不确定,都由上次的功能元素结果来确定
RandomAccessFile:
- 该对象即可读取,又可写入
- 该对象中的定义了一个大型的byte数组,通过定义指针来操作这个数组
- 可以通过该对象的getFilePointer()获取指针的位置,通过seek()方法设置指针的位置
- 该对象操作的源和目的必须是文件
- 其实该对象内部封装了字节读取流和字节写入流
注意:实现随机访问,最好是数据有规律,比如实现多线程下载
静态数据不能被序列化,因为静态数据不在堆内存中,是存储在静态方法区中;如何将非静态的数据不进行序列化?用transient 关键字修饰此变量即可
逻辑端口:用于标识进程的逻辑地址,不同进程的标识;有效端口:0~65535,其中0~1024系统使用或保留端口
反射技术:其实就是动态加载一个指定的类,并获取该类中的所有的内容
- clazz.getMethods();//获取的是该类中的公有方法和父类中的公有方法
- clazz.getDeclaredMethods();//获取本类中的方法,包含私有方法
类和接口的区别: 当一个类在初始化时,要求其父类全部都已经初始化过了,但是一个接口在初始化时,并不要求其父接口全部都完成了初始化,只有真正使用到父接口的时候(如引用接口中定义的常量)才会初始化
wait()、notify()和notifyAll()是 Object类 中的方法 ;Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、 notify(),使用Condition1的await()、signal()这种方式实现线程间协作更加安全和高效。 线程的互斥锁机制:synchronized,lock,condition
数组复制效率: System.arraycopy > clone > System.copyOf > for循环
方法的重写(override)两同两小一大原则:
- 方法名相同,参数类型相同
- 子类返回类型小于等于父类方法返回类型,
- 子类抛出异常小于等于父类方法抛出异常,
- 子类访问权限大于等于父类方法访问权限。
值传递不可以改变原变量的内容和地址;引用传递不可以改变原变量的地址,但可以改变原变量的内容;
Byte,Short,Integer,Long,Character这5种整型的包装类也只是在对应值小于等于127并且大于等于-128时才可使用常量池,因为他们至占用一个字节(-128~127);
再者Integer.valueOf方法中也有判断,如果传递的整型变量>= -128并且小于127时会返回IntegerCache类中一个静态数组中的某一个对象, 否则会返回一个新的Integer对象,代码如下:public static Integer valueOf(int i) assert IntegerCache.high >= 127; if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i);
new Integer(59) 在堆中分配。
() 和 () 中先执行父类,再初始化变量,最后执行语句块。
逻辑运算
- 短路:在逻辑表达式中,如果能通过逻辑运算符左边表达式的值就能推算出整个表达式的值,那么将不再继续执行逻辑运算符右边的表达式。(&&,||)
- 非短路:始终执行逻辑表达式两边的表达式。(&,|)
super.getClass() 得到的依然是runtime当前类,若要得到真正的父类,需要用super.getClass().getSuperclass()
方法重载:同一类中的相同的方法名,参数表必须不同,方法的返回类型、修饰符可以相同,也可不同。
volatile: volatile 变量提供顺序和可见性保证,例如,JVM 或者 JIT为了获得更好的性能会对语句重排序,但是 volatile 类型变量即使在没有同步块的情况下赋值也不会与其他语句重排序。 volatile 提供 happens-before 的保证,确保一个线程的修改能对其他线程是可见的。某些情况下,volatile 还能提供原子性,如读 64 位数据类型,像 long 和 double 都不是原子的,但 volatile 类型的 double 和 long 就是原子的。
在使用 wait() 中,使用 while(需要等待资源条件) 来代替直接使用,这样可以双重判断资源被满足并且被唤醒。
哪个类包含 clone 方法?是 Cloneable 还是 Object?java.lang.Cloneable 是一个标示性接口,不包含任何方法,clone 方法在 object 类中定义。并且需要知道 clone() 方法是一个本地方法,这意味着它是由 c 或 c++ 或 其他本地语言实现的。
a = a + b 与 a += b 的区别: += 隐式的将加操作的结果类型强制转换为持有结果的类型。如果后者相加,如 byte、short 或者 int,首先会将它们提升到 int 类型,然后在执行加法操作。
Switch 中可以使用 String 吗?从 Java 7 开始,我们可以在 switch case 中使用字符串,但这仅仅是一个语法糖。内部实现在 switch 中使用字符串的 hash code。
JIT 代表即时编译(Just In Time
compilation),当代码执行的次数超过一定的阈值时,会将 Java
字节码转换为本地代码,如,主要的热点代码会被准换为本地代码,这样有利大幅度提高 Java 应用的性能。Java 中堆和栈: JVM 中堆和栈属于不同的内存区域,使用目的也不同。栈常用于保存方法帧和局部变量,而对象总是在堆上分配。栈通常都比堆小,也不会在多个线程之间共享,而堆被整个 JVM 的所有线程共享。
LinkedList 中 getFirst()/element()/peek() 返回元素但不移除,当列表为空时,peek() 返回 null 而不抛出异常;removeFirst()/remove()/poll() 都是移除并返回头,当列表为空时,poll() 返回 null 而不抛出异常。
Dalvik VM 并不是一个 Java 虚拟机,它没有遵循 Java 虚拟机规范,不能直接执行 Java 的 Class 文件,使用的是寄存器架构而不是 JVM 中常见的栈架构。但是它与 Java 又有着千丝万缕的联系,它执行的 dex(Dalvik Executable) 文件可以通过 Class 文件转化而来,使用 Java 语法编写应用程序,可以直接使用大部分的 Java API 等。
以上是关于被你忽略掉的 Java 细节知识的主要内容,如果未能解决你的问题,请参考以下文章