关于程序员:Java程序员必修内功心法内功修炼第二层

21次阅读

共计 7561 个字符,预计需要花费 19 分钟才能阅读完成。

1,Java 面向对象

1.1 类和对象

1.1.1. 面向对象和面向过程的区别

1,面向过程:面向过程性能比面向对象高。因为类调用时须要实例化,开销比拟大,比拟耗费资源,所以当性能是最重要的考量因素的时候,比方单片机、嵌入式开发、Linux/Unix 等个别采纳面向过程开发。然而,面向过程没有面向对象易保护、易复用、易扩大。

2,面向对象:面向对象易保护、易复用、易扩大。因为面向对象有封装、继承、多态性的个性,所以能够设计出低耦合的零碎,使零碎更加灵便、更加易于保护。然而,面向对象性能比面向过程低。

这个并不是根本原因,面向过程也须要分配内存,计算内存偏移量,Java 性能差的次要起因并不是因为它是面向对象语言,而是 Java
是半编译语言,最终的执行代码并不是能够间接被 CPU 执行的二进制机械码。

而面向过程语言大多都是间接编译成机械码在电脑上执行,并且其它一些面向过程的脚本语言性能也并不一定比 Java 好。

1.1.2. 结构器 Constructor 是否可被 override?

Constructor 不能被 override(重写), 然而能够 overload(重载), 所以你能够看到一个类中有多个构造函数的状况。

1.1.3. 在 Java 中定义一个不做事且没有参数的构造方法的作用

Java 程序在执行子类的构造方法之前,如果没有用 super() 来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。因而,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 super() 来调用父类中特定的构造方法,则编译时将产生谬误,因为 Java 程序在父类中找不到没有参数的构造方法可供执行。解决办法是在父类里加上一个不做事且没有参数的构造方法。

1.1.4. 成员变量与局部变量的区别有哪些?

1,从语法模式上看: 成员变量是属于类的,而局部变量是在代码块或办法中定义的变量或是办法的参数;成员变量能够被 public,private,static 等修饰符所润饰,而局部变量不能被访问控制修饰符及 static 所润饰;然而,成员变量和局部变量都能被 final 所润饰。

2,从变量在内存中的存储形式来看: 如果成员变量是应用 static 润饰的,那么这个成员变量是属于类的,如果没有应用 static 润饰,这个成员变量是属于实例的。而对象存在于堆内存,局部变量则存在于栈内存。

3,从变量在内存中的生存工夫上看: 成员变量是对象的一部分,它随着对象的创立而存在,而局部变量随着办法的调用而主动隐没。

4,成员变量如果没有被赋初值: 则会主动以类型的默认值而赋值(一种状况例外: 被 final 润饰的成员变量也必须显式地赋值),而局部变量则不会主动赋值。

1.1.5. 创立一个对象用什么运算符? 对象实体与对象援用有何不同?

new 运算符,new 创建对象实例(对象实例在堆内存中),对象援用指向对象实例(对象援用寄存在栈内存中)。一个对象援用能够指向 0 个或 1 个对象(一根绳子能够不系气球,也能够系一个气球); 一个对象能够有 n 个援用指向它(能够用 n 条绳子系住一个气球)。

1.1.6. 一个类的构造方法的作用是什么? 若一个类没有申明构造方法,该程序能正确执行吗? 为什么?

次要作用是实现对类对象的初始化工作。能够执行。因为一个类即便没有申明构造方法也会有默认的不带参数的构造方法。如果咱们本人增加了类的构造方法(无论是否有参),Java 就不会再增加默认的无参数的构造方法了,这时候,就不能间接 new 一个对象而不传递参数了,所以咱们始终在人不知; 鬼不觉地应用构造方法,这也是为什么咱们在创建对象的时候前面要加一个括号(因为要调用无参的构造方法)。如果咱们重载了有参的构造方法,记得都要把无参的构造方法也写进去(无论是否用到),因为这能够帮忙咱们在创建对象的时候少踩坑。

1.1.7. 构造方法有哪些个性?

1, 名字与类名雷同。
2, 没有返回值,但不能用 void 申明构造函数。
3, 生成类的对象时主动执行,无需调用。

1.1.8. 在调用子类构造方法之前会先调用父类没有参数的构造方法, 其目标是?

帮忙子类做初始化工作。

1.1.9. 对象的相等与指向他们的援用相等, 两者有什么不同?

对象的相等,比的是内存中寄存的内容是否相等。而援用相等,比拟的是他们指向的内存地址是否相等。

1.2. 面向对象三大特色

1.2.1. 封装

封装是指把一个对象的状态信息(也就是属性)暗藏在对象外部,不容许内部对象间接拜访对象的外部信息。然而能够提供一些能够被外界拜访的办法来操作属性。就如同咱们看不到挂在墙上的空调的外部的整机信息(也就是属性),然而能够通过遥控器(办法)来管制空调。如果属性不想被外界拜访,咱们大可不必提供办法给外界拜访。然而如果一个类没有提供给外界拜访的办法,那么这个类也没有什么意义了。就如同如果没有空调遥控器,那么咱们就无奈操控空凋制冷,空调自身就没有意义了(当然当初还有很多其余办法,这里只是为了举例子)。
`public class Student {

private int id;//id 属性私有化
private String name;//name 属性私有化

// 获取 id 的办法
public int getId() {return id;}

// 设置 id 的办法
public void setId(int id) {this.id = id;}

// 获取 name 的办法
public String getName() {return name;}

// 设置 name 的办法
public void setName(String name) {this.name = name;}

}
`

1.2.2. 继承

不同类型的对象,相互之间常常有肯定数量的共同点。例如,小明同学、小红同学、小李同学,都共享学生的个性(班级、学号等)。同时,每一个对象还定义了额定的个性使得他们不同凡响。例如小明的数学比拟好,小红的性情惹人青睐;小李的力量比拟大。继承是应用已存在的类的定义作为根底建设新类的技术,新类的定义能够减少新的数据或新的性能,也能够用父类的性能,但不能选择性地继承父类。通过应用继承,能够疾速地创立新的类,能够进步代码的重用,程序的可维护性,节俭大量创立新类的工夫,进步咱们的开发效率。

对于继承如下 3 点请记住:

1, 子类领有父类对象所有的属性和办法(包含公有属性和公有办法),然而父类中的公有属性和办法子类是无法访问,只是领有。

2, 子类能够领有本人属性和办法,即子类能够对父类进行扩大。
3, 子类能够用本人的形式实现父类的办法。(当前介绍)。

1.2.3. 多态

多态,顾名思义,示意一个对象具备多种的状态。具体表现为父类的援用指向子类的实例。

多态的特点:

1, 对象类型和援用类型之间具备继承(类)/ 实现(接口)的关系;

2. 援用类型变量收回的办法调用的到底是哪个类中的办法,必须在程序运行期间能力确定;

3, 多态不能调用“只在子类存在但在父类不存在”的办法;

4, 如果子类重写了父类的办法,真正执行的是子类笼罩的办法,如果子类没有笼罩父类的办法,执行的是父类的办法。

1.3. 修饰符

1.3.1. 在一个静态方法内调用一个非动态成员为什么是非法的?

因为静态方法能够不通过对象进行调用,因而在静态方法里,不能调用其余非动态变量,也不能够拜访非动态变量成员。

1.3.2. 静态方法和实例办法有何不同

1, 在内部调用静态方法时,能够应用 ” 类名. 办法名 ” 的形式,也能够应用 ” 对象名. 办法名 ” 的形式。而实例办法只有前面这种形式。也就是说,调用静态方法能够无需创建对象。

2, 静态方法在拜访本类的成员时,只容许拜访动态成员(即动态成员变量和静态方法),而不容许拜访实例成员变量和实例办法;实例办法则无此限度。

1.5. 其它重要知识点

1.5.1. String StringBuffer 和 StringBuilder 的区别是什么? String 为什么是不可变的?

简略的来说:String 类中应用 final 关键字润饰字符数组来保留字符串,private final char value[],所以 String 对象是不可变的。

补充(来自 issue 675):在 Java 9 之后,String 类的实现改用 byte 数组存储字符串 private final
byte[] value;

而 StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是应用字符数组保留字符串 char[]value 然而没有用 final 关键字润饰,所以这两种对象都是可变的。

StringBuilder 与 StringBuffer 的构造方法都是调用父类构造方法也就是 AbstractStringBuilder 实现的,大家能够自行查阅源码。

AbstractStringBuilder.java
`abstract class AbstractStringBuilder implements Appendable, CharSequence {

/**
 * The value is used for character storage.
 */
char[] value;

/**
 * The count is the number of characters used.
 */
int count;

AbstractStringBuilder(int capacity) {value = new char[capacity];
}}

`
线程安全性

String 中的对象是不可变的,也就能够了解为常量,线程平安。AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacity、append、insert、indexOf 等公共办法。StringBuffer 对办法加了同步锁或者对调用的办法加了同步锁,所以是线程平安的。StringBuilder 并没有对办法进行加同步锁,所以是非线程平安的。

性能

每次对 String 类型进行扭转的时候,都会生成一个新的 String 对象,而后将指针指向新的 String 对象。StringBuffer 每次都会对 StringBuffer 对象自身进行操作,而不是生成新的对象并扭转对象援用。雷同状况下应用 StringBuilder 相比应用 StringBuffer 仅能取得 10%~15% 左右的性能晋升,但却要冒多线程不平安的危险。

对于三者应用的总结:

1, 操作大量的数据: 实用 String
2, 单线程操作字符串缓冲区下操作大量数据: 实用 StringBuilder
3, 多线程操作字符串缓冲区下操作大量数据: 实用 StringBuffer

1.5.2. Object 类的常见办法总结

Object 类是一个非凡的类,是所有类的父类。它次要提供了以下 11 个办法:
`public final native Class<?> getClass()//native 办法,用于返回以后运行时对象的 Class 对象,应用了 final 关键字润饰,故不容许子类重写。

public native int hashCode() //native 办法,用于返回对象的哈希码,次要应用在哈希表中,比方 JDK 中的 HashMap。
public boolean equals(Object obj)// 用于比拟 2 个对象的内存地址是否相等,String 类对该办法进行了重写用户比拟字符串的值是否相等。

protected native Object clone() throws CloneNotSupportedException//naitive 办法,用于创立并返回以后对象的一份拷贝。个别状况下,对于任何对象 x,表达式 x.clone() != x 为 true,x.clone().getClass() == x.getClass() 为 true。Object 自身没有实现 Cloneable 接口,所以不重写 clone 办法并且进行调用的话会产生 CloneNotSupportedException 异样。

public String toString()// 返回类的名字 @实例的哈希码的 16 进制的字符串。倡议 Object 所有的子类都重写这个办法。

public final native void notify()//native 办法,并且不能重写。唤醒一个在此对象监视器上期待的线程 ( 监视器相当于就是锁的概念)。如果有多个线程在期待只会任意唤醒一个。

public final native void notifyAll()//native 办法,并且不能重写。跟 notify 一样,惟一的区别就是会唤醒在此对象监视器上期待的所有线程,而不是一个线程。

public final native void wait(long timeout) throws InterruptedException//native 办法,并且不能重写。暂停线程的执行。留神:sleep 办法没有开释锁,而 wait 办法开释了锁。timeout 是等待时间。

public final void wait(long timeout, int nanos) throws InterruptedException// 多了 nanos 参数,这个参数示意额定工夫(以毫微秒为单位,范畴是 0-999999)。所以超时的工夫还须要加上 nanos 毫秒。

public final void wait() throws InterruptedException// 跟之前的 2 个 wait 办法一样,只不过该办法始终期待,没有超时工夫这个概念

protected void finalize() throws Throwable {}// 实例被垃圾回收器回收的时候触发的操作
`

1.5.3. == 与 equals(重要)

== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象 (根本数据类型比拟的是值,援用数据类型比拟的是内存地址)。

equals() : 它的作用也是判断两个对象是否相等。但它个别有两种应用状况:

状况 1:类没有笼罩 equals() 办法。则通过 equals() 比拟该类的两个对象时,等价于通过“==”比拟这两个对象。
状况 2:类笼罩了 equals() 办法。个别,咱们都笼罩 equals() 办法来比拟两个对象的内容是否相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。

举个例子:

`public class test1 {

public static void main(String[] args) {String a = new String("ab"); // a 为一个援用
    String b = new String("ab"); // b 为另一个援用, 对象的内容一样
    String aa = "ab"; // 放在常量池中
    String bb = "ab"; // 从常量池中查找
    if (aa == bb) // true
        System.out.println("aa==bb");
    if (a == b) // false,非同一对象
        System.out.println("a==b");
    if (a.equals(b)) // true
        System.out.println("aEQb");
    if (42 == 42.0) { // true
        System.out.println("true");
    }
}

}
`
阐明:

1,String 中的 equals 办法是被重写过的,因为 object 的 equals 办法是比拟的对象的内存地址,而 String 的 equals 办法比拟的是对象的值。

2, 当创立 String 类型的对象时,虚构机会在常量池中查找有没有曾经存在的值和要创立的值雷同的对象,如果有就把它赋给以后援用。如果没有就在常量池中从新创立一个 String 对象。

1.5.4. hashCode 与 equals (重要)

面试官可能会问你:“你重写过 hashcode 和 equals 么,为什么重写 equals 时必须重写 hashCode 办法?”

2.5.4.1. hashCode()介绍

hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个 int 整数。这个哈希码的作用是确定该对象在哈希表中的索引地位。hashCode() 定义在 JDK 的 Object.java 中,这就意味着 Java 中的任何类都蕴含有 hashCode() 函数。

散列表存储的是键值对 (key-value),它的特点是:能依据“键”疾速的检索出对应的“值”。这其中就利用到了散列码!(能够疾速找到所须要的对象)

2.5.4.2. 为什么要有 hashCode

咱们先以“HashSet 如何查看反复”为例子来阐明为什么要有 hashCode:当你把对象退出 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象退出的地位,同时也会与该地位其余曾经退出的对象的 hashcode 值作比拟,如果没有相符的 hashcode,HashSet 会假如对象没有反复呈现。然而如果发现有雷同 hashcode 值的对象,这时会调用 equals() 办法来查看 hashcode 相等的对象是否真的雷同。如果两者雷同,HashSet 就不会让其退出操作胜利。如果不同的话,就会从新散列到其余地位。(摘自我的 Java 启蒙书《Head first java》第二版)。这样咱们就大大减少了 equals 的次数,相应就大大提高了执行速度。

通过咱们能够看出:hashCode() 的作用就是获取哈希码,也称为散列码;它实际上是返回一个 int 整数。这个哈希码的作用是确定该对象在哈希表中的索引地位。hashCode() 在散列表中才有用,在其它状况下没用。在散列表中 hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的地位。

2.5.4.3. hashCode()与 equals()的相干规定

1, 如果两个对象相等,则 hashcode 肯定也是雷同的
2, 两个对象相等, 对两个对象别离调用 equals 办法都返回 true
3, 两个对象有雷同的 hashcode 值,它们也不肯定是相等的
4, 因而,equals 办法被笼罩过,则 hashCode 办法也必须被笼罩
5,hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即便这两个对象指向雷同的数据)

2.5.5. Java 序列化中如果有些字段不想进行序列化,怎么办?

对于不想进行序列化的变量,应用 transient 关键字润饰。

transient 关键字的作用是:阻止实例中那些用此关键字润饰的的变量序列化;当对象被反序列化时,被 transient 润饰的变量值不会被长久化和复原。transient 只能润饰变量,不能润饰类和办法。

2.5.6. 获取用键盘输入罕用的两种办法

办法 1:通过 Scanner

办法 2:通过 BufferedReader

正文完
 0