Java 面向对象编程三大特性: 封装 继承 多态

封装

封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访

问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没

有什么意义了。

继承

继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也

可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码。

于继承如下 3 点请记住:

1. 子类拥有父类非 private 的属性和方法。

2. 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。

3. 子类可以用自己的方式实现父类的方法。(以后介绍)。多态

所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并

不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出

的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。

 Java 中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口

中同一方法)。

String StringBuffer  StringBuilder 的区别是什么

String 为什么是不可变的

可变性

简单的来说:String 类中使用 final 关键字字符数组保存字符串,private

final char value[],所以 String 对象是不可变的。而 StringBuilder

StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder

也是使用字符数组保存字符串char[]value 但是没有用 final 关键字修饰,所以这两种对象都是可变的。

StringBuilder  StringBuffer 的构造方法都是调用父类构造方法也就是 AbstractStringBuilder 实现

的,大家可以自行查阅源码。

AbstractStringBuilder.java

线程安全性

String 中的对象是不可变的,也就可以理解为常量,线程安全。

AbstractStringBuilder  StringBuilder  StringBuffer 的公共父类,定义了一些字符串的基本操作,

 expandCapacityappendinsertindexOf 等公共方法。StringBuffer 对方法加了同步锁或者对

调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程

安全的。 性能

每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String

象。StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。

相同情况下使用

StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风

险。对于三者使用的总结:

1. 操作少量的数据 = String

2. 单线程操作字符串缓冲区下操作大量数据 = StringBuilder

3. 多线程操作字符串缓冲区下操作大量数据 = StringBuffer

自动装箱与拆箱

abstract class AbstractStringBuilder implements Appendable, CharSequence {

char[] value;

int count;

AbstractStringBuilder() {

}

AbstractStringBuilder(int capacity) {

value = new char[capacity];

}装箱:将基本类型用它们对应的引用类型包装起来;拆箱:将包装类型转换为基本数据类型;

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

由于静态方法可以不通过对象进行调用,因此在静态方法里,不能调用其他非静态变量,也不可以访问

非静态变量成员。

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

Java 程序在执行子类的构造方法之前,如果没有用 super() 来调用父类特定的构造方法,则会调用父

类中没有参数的构造方法。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又

没有用 super() 来调用父类

中特定的构造方法,则编译时将发生错误,因为 Java 程序在父类中找不到没有参数的构造方法可供执

行。解决办法是在父类里加上一个不做事且没有参数的构造方法。

import java  javax 有什么区别

刚开始的时候 JavaAPI 所必需的包是 java 开头的包,javax 当时只是扩展

API 包来说使用。然而随着时间的推移,javax 逐渐的扩展成为 Java API 的组

成部分。但是,将扩展从 javax 包移动到 java 包将是太麻烦了,最终会破坏

一堆现有的代码。因此,最终决定 javax 包将成为标准 API 的一部分。

所以,实际上 java  javax 没有区别。这都是一个名字。

接口和抽象类的区别是什么

1. 接口的方法默认是 public,所有方法在接口中不能有实现(Java 8 开始

接口方法可以有默认实现),抽象类可以有非抽象的方法

2. 接口中的实例变量默认是 final 类型的,而抽象类中则不一定

3. 一个类可以实现多个接口,但最多只能实现一个抽象类

4. 一个类实现接口的话要实现接口的所有方法,而抽象类不一定

5. 接口不能用 new 实例化,但可以声明,但是必须引用一个实现该接口的对象 从设计层面来说,抽

象是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范。

成员变量与局部变量的区别有那些

1. 从语法形式上,看成员变量是属于类的,而局部变量是在方法中定义的变量或是方法的参数;成员

变量可以被 public,private,static 等修饰符所

修饰,而局部变量不能被访问控制修饰符及 static 所修饰;但是,成员

变量和局部变量都能被 final 所修饰;

2. 从变量在内存中的存储方式来看,成员变量是对象的一部分,而对象存在于堆内存,局部变量存在

于栈内存

3. 从变量在内存中的生存时间上看,成员变量是对象的一部分,它随着对象的创建而存在,而局部变

量随着方法的调用而自动消失。

4. 成员变量如果没有被赋初值,则会自动以类型的默认值而赋值(一种情况例外被 final 修饰的成员

变量也必须显示地赋值);而局部变量则不

会自动赋值。

创建一个对象用什么运算符?对象实体与对象引用有何不同?new 运算符,new 创建对象实例(对象实例在堆内存中),对象引用指向对象实例(对象引用存放在栈

内存中)。一个对象引用可以指向 0 个或 [1] 个对象

(一根绳子可以不系气球,也可以系一个气球);一个对象可以有 n 个引用指向

它(可以用 n 条绳子系住一个气球)。

1 . 名字与类名相同;

什么是方法的返回值?返回值在类的方法里的作用是什么?

方法的返回值是指我们获取到的某个方法体中的代码执行后产生的结果!(前提是该方法可能产生结

果)。返回值的作用:接收出结果,使得它可以用于其他的操作!

一个类的构造方法的作用是什么 若一个类没有声明构造方

,该程序能正确执行吗 ?为什么?

主要作用是完成对类对象的初始化工作。可以执行。因为一个类即使没有声明构造方法也会有默认的不

带参数的构造方法。

构造方法有哪些特性

1. 名字与类名相同

2. 没有返回值,但不能用 void 声明构造函数;

3. 生成类的对象时自动执行,无需调用。

静态方法和实例方法有何不同

1. 在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"

象名.方法名"的方式。而实例方法只有后面这种方式。也就是说,调用

静态方法可以无需创建对象。

2. 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访

问实例成员变量和实例方法;实例方法则无此限制.

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

对象的相等,比的是内存中存放的内容是否相等。而引用相等,比较的是他们指向的内存地址是否相

等。

在调用子类构造方法之前会先调用父类没有参数的构造方

法,其目的是?

帮助子类做初始化工作。

==  equals(重要)

== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同

一个对象。(基本数据类型==比较的是值,引用数据类型==比较的是内存地址)

equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:

情况 1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象。

情况 2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来

两个对象的内容相等;若它们的内容相等,则返回 true (即,认为这两

个对象相等)

举个例子:

说明:

• String 中的 equals 方法是被重写过的,因为 object  equals 方法是

比较的对象的内存地址,而 String  equals 方法比较的是对象的值。

当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存

在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个

String 对象。

hashCode  equals(重要)

面试官可能会问你:你重写过 hashcode  equals 么,为什么重写 equals

时必须重写 hashCode 方法?

hashCode()介绍

hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个 int 整数。这个哈希码的作用是

确定该对象在哈希表中的索引位置。hashCode() 定义

 JDK  Object.java 中,这就意味着 Java 中的任何类都包含有 hashCode() 函数。

散列表存储的是键值对(key-value),它的特点是:能根据快速的检索出对应的。这其中就利用到

了散列码!(可以快速找到所需要的对象)

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");

}

}

}为什么要有 hashCode

我们以“HashSet 如何检查重复为例子来说明为什么要有 hashCode

当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断

对象加入的位置,同时也会与其他已经加入的对象的 hashcode 值作比较,如

果没有相符的 hashcodeHashSet 会假设对象没有重复出现。但是如果发现有

相同 hashcode 值的对象,这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同。如

果两者相同,HashSet 就不会让其加入操作成功。

如果不同的话,就会重新散列到其他位置。(摘自我的 Java 启蒙书《Head

first java》第二版)。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。

hashCode()与 equals()的相关规定

1. 如果两个对象相等,则 hashcode 一定也是相同的

2. 两个对象相等,对两个对象分别调用 equals 方法都返回 true

3. 两个对象有相同的 hashcode 值,它们也不一定是相等的

4. 因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖

5. hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两

个对象无论如何都不会相等(即使这两个对象指向相同的数据)

关于final关键字的一些总结

final 关键字主要用在三个地方:变量、方法、类。

1. 对于一个 final 变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果

是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。

2. 当用 final 修饰一个类时,表明这个类不能被继承。final 类中的所有成员方法都会被隐式地指定为

final 方法。

3. 使用 final 方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个

原因是效率。在早期的 Java 实现版本中,会将final 方法转为内嵌调用。但是如果方法过于庞大,

可能看不到内嵌调用带来的任何性能提升(现在的 Java 版本已经不需要使用 final 方法进行这些优

化了)。类中所有的 private 方法都隐式地指定为 final

05-04 05:29