原文:JavaGuide
2.1 Java 基础
1. 面向对象和面向过程的区别
- 面向过程:面向过程的性能比面向对象的高。因为累调用时需要实例化,开销比较大,比较消耗资源。但面向过程没有想象对象易维护、易复用、易扩展。
- 面向对象:面向对象易维护、易复用、易扩展。因为面向对象有封装、继承、多态的特性。
注意: Java 性能差的主要原因不是因为它是面向对象语言,而是因为Java是半编译语言,最终执行的代码并不是CPU直接执行的二进制。
2. Java 语言有哪些特点
- 1.简单易学
- 2.面向对象(封装、继承、多态)
- 3.平台无关性(Java虚拟机)
- 4.可靠性
- 5.安全性
- 6.支持多线程(C++需要调用操作系统的多线程功能)
- 7.支持网络编程并且很方便。
- 8.编译与解释并存
3. JVM、JDK、JRE
JVM
Java虚拟机(JVM)是运行Java字节码的虚拟机。JVM 有针对不同系统的特定实现(Windows、Linux、MacOS),目的是使用相同的字节码,他们都会给出相同的结果。
Java字节码
在Java中,JVM可以理解的代码就叫做字节码(扩展名为.class的文件),它不面向任何特定的处理器,只面向虚拟机。
Java 语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以Java程序运行时比较高效,并且无序重新编译便可在不同操作系统的计算机上运行。
Java 代码编译:
Java字节码 == JVM ==> 二进制机器码时,JVM类加载器首先加载字节码文件,然后通过解释器逐行解释执行。这种方式的执行速度相对比较慢。而且有些方法和代码块是经常需要被调用的(热点代码),所以后面引进了JIT编译器。JIT属于运行时编译,JIT完成一次编译后,会将字节码对应的机器码保存下来,下次可以直接使用。
Java字节码和不同系统的JVM实现是Java语言“Write Once,Run Anywhere”的关键所在。
JDK 和 JRE
JRE:Java运行时环境。它是运行已编译Java 程序所需的所有内容的集合。包括Java虚拟机(JVM)、Java类库、Java命令和其他一些基础构件。但是它不能用于创建新程序。
JDK:Java Development Kit,它是功能齐全的Java SDK。他包含Java运行环境(JRE)、编译器(javac)和工具(javadoc和jdb)。JDK能够创建和编译程序。
4. Oracle JDK 和 OpenJDK的对比
对于Java 7, 这两个没有关键不同的地方。
OpenJDK 是基于Sum捐赠的HotSpot源代码。
-
- Open JDK 是一个参考模型并且是完全开源的,而Oracle JDK是 OpenJDK的一个实现,并不是完全开源的。
-
- Oracle JDK 比 Open JDK 更稳定。Open JDK 和 Oracle JDK 的代码几乎相同,但 Oracle JDK 有更多的类和一些bug修复,并且Oracle JDK 经过了彻底的测试。
-
- 在响应性和JVM性能方面,Oracle JDK 相比 Open JDK 提供了更好的性能。
5. Java 和 C++ 的区别
- 1.都是面向对象的语言,都支持封装、继承和多态。
-
- Java不提供指针来直接访问内存,程序内存更加安全。
-
- Java的类是单继承的,C++支持多继承。不过虽然Java的类不支持多继承,但接口可以多继承。
- 4.C语言中,字符串或字符数组的最后有一个结束符
'\0'
,Java中没有结束符的概念。
6.Java程序的主类
一个程序可以有多个类,但是只能有一个类是主类,包含main()
方法。
7、Java 应用程序 VS Java 小程序
- Java 应用程序:从主线程main()方法启动
- Java 小程序: 没哟main方法,主要是嵌在浏览器上面运行(调用 init()或者run()来启动)
8. 字符型常量 VS 字符串常量
字符型常量 | 字符串常量 | |
---|---|---|
形式 | 单引号表示的一个字符,例如 'k' |
双引号引起的若干个字符,例如"linyk3" |
含义 | 相当于一个整型值,可以参加表达式运算 | 代表一个地址,表示该字符串在内存中存放的位置 |
内存大小 | Java中char占2个字节 | 若干个字节 |
注意:Java中每一种基本类型所占的存储空间是不变的:
9. 构造器Constructor 能否被重写override?
Constructor 不能被override(重写),但是可以被overload(重载),所以你看到一个类中有多个构造函数的情况。
10.重写和重载的区别
- 重载:发生在同一个类中。方法名相同,参数类型、参数个数、参数顺序不同。 方法的返回值和访问修饰符可以不同。
- 重写:发生在子类中。子类对父类的允许访问的方法的实现过程进行重新编写。方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符大于等于父类。(方法提供的行为改变,而方法的外貌并没有改变)
11.Java 面向对象编程的三大特性: 封装、继承、多态
封装
封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法。
继承
继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以使用父类的可继承的所有功能。通过继承我们可以非常方便的复用以前的代码。
- 1.子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问的,只是拥有。
- 2.子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
- 3.子类可以用自己的方式实现父类的方法。
多态
多态指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定。即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在程序运行期间才能决定。
在Java 中有两种形式可以实现多态:
- 1.继承:多个子类对同一个方法的重写。
- 2.接口:实现接口并覆盖接口中的同一方法。
12.String、StringBuffer 和 StringBuilder
String :
Java 9 之前: private final char[] value
Java 9 之后: private final byte[] value
StringBuilder 和 StringBuffer 都继承自 AbstractStringBuilder 类,char[] value
,没有使用 final
关键字。
每次对String类型进行改变的时候,都会生成一个新的String对象,然后将变量指向新的String对象。StringBuffer 每次都会对自己进行操作,而不是生成新的对象。
特性 | String | StringBuffer | StringBuilder |
---|---|---|---|
可变性 | 不可变 | 可变 | 可变 |
线程安全型 | 不可变常量,线程安全 | 同步锁,线程安全 | 非线程安全 |
性能 | 性能慢 | 性能快 | 比StringBuffer性能提升10%-15% |
总结:
- 1.操作少量数据:适用String
- 2.单线程操作大量数据:适用StringBuilder
- 3.多线程操作大量数据:适用StringBuffer;
13.自动装箱和拆箱
- 装箱:基本类型 => 包装类型
- 拆箱:包装类型 => 基本类型
14.静态方法内调用一个非静态成员是非法的
静态方法可以通过类调用,不必通过生成对象来进行调用。
而非静态成员需要通过对象来调用。
15.Java定义一个无参构造方法的作用
Java 程序在执行子类的构造方法之前,如果没有用 super()
来调用父类特定的构造方法,则会调用父类中无参构造方法,因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中没有用super() 来调用父类中特定的构造方法,则编译时间发生错误:Java程序在父类中找不到无参构造方法可供执行。
解决方案就是在父类中加上一个不做事且没有参数的构造方法。
16. 略过
17. 接口和抽象类
- 1.接口的方法默认是 public的,所有方法在接口中不能有实现(Java 8 开始接口方法可以有默认实现),而抽象类可以有非抽象的方法。
- 2.接口中除了static、final 变量,不能有其他变量,而抽象类中则不一定。
- 3.一个类中可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过 extends 关键字扩展多个接口。
- 4.接口方法默认修饰符是public,抽象方法可以有public、protected和default这些修饰符。(抽象类就是为了被重写,所以不能使用private 关键字修饰)
- 5.设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。
备注:
1.JDK8中,接口也可以定义静态方法,可以直接用接口名调用。实现类和实现是不可以调用的。 如果直接实现两个接口,接口中定义了一样的默认方法,则必须重写,不然会报错。
2.JDK9中,接口被允许定义私有化方法。
Java 接口在JDK7 => JDK9 的变化:
- 1.JDK7或之前的版本,接口里面只能有常量变量和抽象方法。这些接口方法必须由实现类来实现。
- 2.JDK8 接口允许有默认方法和静态方法功能
-
- JDK9 在接口中引入了私有方法和私有静态方法。
18.成员变量 VS 局部变量
成员变量: 属于类,可以被public、private、static等修饰符所修饰。
局部变量: 方法中定义的变量或方法的参数,不能被访问控制修饰符及static修饰。
但是成员变量和局部变量都能被final所修饰。
成员变量如果是static 修饰,则属于类的变量,如果没有使用staitc修饰,则属于实例对象的。对象是存在于堆内存,局部变量存在于栈内存。
成员对象是对象的一部分,随对象的创建而存在,而局部变量随着方法的调用而自动消失。
如果成员变量没有被赋初始值,则会自动以类型的默认值赋值(如果是final,则必须被显式的赋值)。局部变量不会自动赋值。
19.创建对象
使用 new
运算符来创建对象实例。对象实例存储在堆内存中,对象引用存储在栈内存中,对象引用指向对象实例。
20.返回值
方法的返回值:指我们获取都的某个方法中的代码执行后产生的结果
返回值的作用:接收结果,并用于其他操作
21. 构造方法
类的构造方法:作用是完成对类对象的初始化工作。
如果一个类么有声明构造方法,也可以正确执行。因为一个类即使没有声明构造方法,也会有默认的无参构造方法。
22. 构造方法的特性
- 1.函数名与类名相同
- 2.没有返回值,也不能用void声明构造函数
- 3.生成类的对象时自动执行,无需调用。
23.静态方法 VS 实例方法
- 可以通过类或对象来调用静态方法:
类名.方法名
,对象名.方法名
- 调用实例方法是能通过
对象名.方法名
,需要创建对象后才能调用。
静态方法在访问本类的成员时,只能访问静态成员,而不允许访问实例成员变量和实例方法。实例方法无此限制。
24.对象的相等 VS 引用的相等
对象相等:内存中存放的内容是否相等
引用相等:指向的内存地址是否相等
25.子类构造方法里调用父类无参构造方法
在调用子类构造方法之前会调用父类无参构造方法,目的是为了帮助子类做初始化工作。
26. == VS equals
- == : 判断两个对象是不是相等。(基本数据类型比较的是值,引用类型比较的是内存地址)
- equals: 判断两个对象是否相等。Object.equals() 默认是等价于 "==",比较两个对象的内存地址。一般会覆盖 equals 方法来比较两个对象的内容。
public class test {
public static void main(String[] args) {
String a = new String("ab"); // a 为一个引用
String b = new String("ab"); // b 为另一个引用,内容一样,内存地址不一样
String aa = "ab"; // 放在常量池中
String bb = "ab"; // 从常量池中查找
System.out.println(a == b); // false
System.out.println(aa == bb); // true
System.out.println(a.equals(b)); // true
System.out.println(42 == 42.0); // true
}
}
String 的equals 方法是被重写过的,比较的是两个对象的内容。
当创建String类型的对象时,虚拟机在常量池中查找有没有已经存在的值, 如果有就直接赋值给当前引用, 没有就重新创建一个String对象。
27 hashCode 与 equals
- hashCode() : 是在Object 对象中的函数,可以获取哈希码,也称散列码,是一个int整数。在哈希表中用来确定索引的位置,在其他地方没有作用。
hashCode() 默认的行为是对堆上的对象产生独特的值。如果没有重写hashCode(), 则该class的两个对象无论如何都不会相等。
28.Java中只有值传递
29.线程、进程、程序
程序:含有指令和数据的文件,程序是静态的代码。
进程:是系统运行的基本单位,是操作系统分配CPU时间、内存空间、文件和输入输出设备使用权的单位。
线程:线程是进程划分为更小的运行单位,一个进程可以包含1个或多个线程。
30. 线程基本状态
Java 线程在运行的生命周期有6种不同的状态:
状态名称 | 说明 |
---|---|
new | 初始状态,线程被构建,但是还没有调用start()方法 |
runnable | 运行状态,Java线程将就绪和运行时笼统的成为运行中 |
blocked | 阻塞状态,线程阻塞于锁 |
waiting | 等待状态,线程进入等待状态,需要等待其他线程做出一些特定动作(通知或中断) |
time_waiting | 超时等待状态,相比于wating,它可以在指定时间内自行返回 |
terminated | 终止状态,表示当前线程已经执行完毕 |
31.final 关键字
- 1.final 修饰变量:不可变。如果是基本类型,则其数值在初始化之后不能更改,如果是引用类型,则在初始化之后不能指向另一个对象。
- 2.final 修饰方法:一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率,因为早期的Java会将final转化为内嵌调用。类中所有private的方法都隐式地指定为final。
- 3.final 修饰类: 表示这个类不能被继承,final类中的所有成员方法都会被隐式的指定为final方法。
32. Java 异常
Java中,所有的异常都有一个共同的祖先:java.lang.Throwable
类。它有两个重要的子类Exception(异常)
和 Error(错误)
。
- Error(错误):是程序无法处理的错误,表示运行应用程序中较严重的问题。
- Exception(异常):程序本身可以处理的异常。
异常处理:
- try:用来捕获异常,其后可接0个或多个catch块,如果没有catch块,则必须接一个finally块。
- catch: 用来处理try 捕获到的异常。
- finally:无论是否捕获或处理异常,finally的语句都会被执行。当在try 块或 catch 块遇到 return语句时,finally 语句块将在方法返回之前被执行。
finally 块不会被执行的情况:
- 1.在finally语句块的第一行发生了异常。
- 2.已经执行了 System.exit(int) 退出程序。
- 3.程序所在的程序死亡。
- 4.关闭CPU。
注意:当try语句和finally语句都有return语句时,在方法返回之前,finally语句的内容将被执行,并且finally语句的返回值会覆盖原来的返回值:
public static int f(int value) {
try {
return value * value;
} finally {
if(value == 2) {
return 0;
}
}
}
如果调用f(2), 返回值将是0.
33.Java 序列化
当一个类实现了serializable接口,如果没有显式的定义 serialVersionUID,Java 序列化机制会根据编译的Class自动生成一个serialVersionUID,用作序列化版本比对。如果Class文件没有发生变化,就算编译多次, serialVersionUID 也不会变化。
如果我们不想通过编译来强制划分软件版本,集实现序列化接口的实体能够兼容先前的版本,就需要显式的定义序列化版本UID,这样即使Class 文件变化了,只要serialVersionUID的值不变,就可以进行正确的序列化和反序列化。
private static final long serialVersionUID = 1L
对于不想进行序列化的变量,可以使用 transient
关键字修饰。
transient
:阻止实例中用此关键字修饰的变量序列化。当反序列化时,被修饰的变量不会被持久化和恢复。
只能修饰变量,不能修饰类和方法。
34. 获取键盘输入的两种方法
方法1. 通过Scanner
Scanner input = new Scanner(System.in);
String s = input.nextLine();
input.close();
方法2. 通过BufferedReader
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String s = input.readLine();
35.Java中IO流
Java中IO流分为几种?
- 按流的流向分,可以分为输入流和输出流;
- 按操作单元分,可以分为字节流和字符流;
- 按流的角色分,可以分为节点流和处理流。
Java IO流共涉及40多个类,都是从如下4个抽象类基类中派生出来的:
- InputStream/ Reader:所有输入流的基类,前者是字节输入流,后者是字符输入流。
- OutputStream/ Writer:所有输出流的基类,前者是字节输出流,后者是字符输出流。
既然有了字节流,为什么还要有字符流
不管是文件读写还是网络发送和接收,信息的最小存储单元都是字节。
字符流是由Java虚拟机将字节转换得到的,但是转换过程非常耗时,并且很容易因为编码类型出现编码问题,所有干脆提供直接操作字符的接口,方便平时对字符进行流操作。
如果是音频文件、图片等媒体文件,建议用字节流;
如果涉及到字符,建议用字符流。
BIO,NIO,AIO有什么区别
BIO(Blocking I/O):同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。适用于活动连接数不是特别高(小于单机1000)的情况,可以让每一个连接专注于自己的I/O并且编程模型简单,也不用考虑系统的过载、限流等问题。线程池本身就是一个天然漏斗,可以缓冲一些系统处理不了的连接或请求。但是当面对十万甚至百万级连接时,就不适用了,需要一种更高效的I/O处理模型来应对更高的并发量。
-
NIO(New I/O): 同步非阻塞I/O模式,NIO的N可以理解为 Non-blocking, 不单纯是New。它支持面向缓冲的,基于通道的I/O操作方法。 在Java1.4引入NIO框架,对应 java.nio 包,提供了Channel,Selector, Buffer等抽象。NIO 提供了与传统BIO模型中 Socket 和 ServerSocket 相对应的 SocketChannel 和 ServerSocketChannel 两种不同的套接字通道实现。 两种通道都支持阻塞(性能和可靠性不好)和非阻塞(性能和可靠性好)两种模式。
- 低负载、低并发 => 采用BIO模式
- 高负载、高并发 => 采用NIO非阻塞模式
AIO(Asynchronous I/O): AIO异步非阻塞IO模型。是基于事件和回调机制实现的,也即是应操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。AIO也就是NIO2,Java7中引入NIO的改进版NIO2。
36. 常见关键字总结:final/static/this/super
final 关键字
final 关键字主要用在3个地方:变量、方法、类。
- 1.final修饰变量:如果是基本类型,则其数值在初始化之后便不能修改;如果是引用类型,则在初始化之后不能再指向另一个对象。
- 2.final修饰方法:第一个原因是把方法锁定,以防任何继承的类修改它的含义。第二个原因是效率,早期Java会把final 方法转为内嵌调用,现在的Java已经不采用了内嵌优化了。类中所有的private 方法都隐式的指定为final。
- 3.final修饰类:表明这个类已经不能被继承。final类中的所有成员方法都会被隐式的指定为final方法。
static 关键字
static 关键字主要有以下四种使用场景:
- 1.修饰成员变量和成员方法:被static修饰的成员属于类,不属于单个对象,成员被所有对象共享,并且可以通过类名直接调用。静态变量存放在Java内存区域的方法区。调用格式:
类名.静态变量名
或类名.静态方法名()
- 2.静态代码块:静态代码块定义在类的方法外,不管创建多少对象,静态代码块只执行一次。静态代码块在非静态代码块之前执行。(静态代码块 => 非静态代码块 => 构造方法)
- 3.静态内部类:static修饰类的话只能修饰内部类,静态内部类和非静态内部类之间一个最大的区别就是:非静态内部类在编译完成之后会隐含的保存着一个引用,指向创建它的外围类。但是静态内部类没有这个引用,这也意味着1.静态内部类的创建不需要依赖外围类的创建;2.它不能使用任何外围类的非static成员变量和方法。
// 静态内部类实现单例模式
public class Singleton {
// 声明为 private 避免调用默认构造方法创建对象
private Singleton() {}
// 声明为private,表明静态内部类只能在Singleton类中访问
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getUniqueInstance() {
return SingletonHolder.INSTANCE;
}
}
- 4.静态导包:Java1.5之后的新特性,可以用来导入类中的静态资源。格式为
import static
这两个关键字连用可以指定导入某个类中的指定静态资源,并且不需要使用类名调用类中的静态成员,可以直接使用类中静态成员变量和方法。
this 关键字
this 关键字用于引用类的当前实例,例如
class Manager {
Employees[] employees;
void manageEmployees() {
int totalEmp = this.employees.length;
System.out.println("Total employees: " + totalEmp)'
this.report();
}
void report() { }
}
上面的例子中,this关键字用于两个地方:
- this.employees.length: 访问类Manager的当前实例的变量。
- this.report(): 调用Manager的当前实例的方法。
this 关键字在这里也是可以省略的。
super 关键字
super 关键字用于从子类访问父类的变量和方法,例如:
public class SupperClass {
protected int number;
protected showNumber() {
System.out.println("number = " + number);
}
}
public class SubClass extends SupperClass {
void bar() {
super.number = 10;
super.showNumber();
}
}
上面的例子中,SubClass 访问父类的成员变量number 并调用父类的showNumber() 方法。
注意:在构造方法中使用super() 调用父类中的其他构造方法时,该语句必须处于构造器的首行,否则编译器会报错。另外,this调用本类中其他构造方法时,也要放在首行。
this 和 super 不能用在static 方法中。
37.Collections 工具类和Arrays 工具类常见方法总结
Collections 工具类常用方法
- 1.排序
- 2.查找
- 3.同步控制(不推荐,需要线程安全的集合类型时请考虑使用JUC包下的并发集合)
排序操作
void reverse(List list); // 反转
void shuffle(List list); // 随机排序
void sort(List list); // 按照自然排序的升序
void sort(List list, Comparator c); // 定制排序,由Comparator控制排序逻辑
void swap(List list, int i, int j); // 交换两个索引位置的元素
void swap(List list, int distance); // 旋转,当distance为整数,将list后distance个元素整体移到前面,当distance为负数,将list的前distance个元素整体移到后面。
查找、替换操作:
int binarySearch(List list, Object key); // 二分查找,返回索引,注意list要求是有序的
int max(Collection coll); // 根据元素的自然顺序,返回最大的元素
int max(Collection coll, Comparator c); // 根据定制顺序,返回最大的元素
void fill(List list, Object obj); //用指定元素obj替换list中所有元素
void frequency(Collection c, Object o) ; // 统计元素c出现的次数
void indexOfSubList(List list, List target); // 统计target在list中第一次出现的索引,找不到就返回-1. 类比于 int lastIndexOfSubList(List list, List target)
boolean replaceAll(List list, Object oldVal, Object newVal); // 用新元素替换旧元素
同步控制
Collections 提供了多个synchronizedXxx()
方法,该方法可以将指定集合包装成线程同步的集合,从而解决多线程并发访问集合时的线程安全问题。不过最好不要使用,英文效率非常低,建议使用JUC包下的并发集合。
HashSet、TreeSet、ArrayList、LinkedList、HashMap、TreeMap 都是线程不安全的。
synchronizedCollection(Collection c); // 返回线程安全的collection
synchronizedList(List list); // 返回线程安全的list
synchronizedMap(Map map); // 返回线程安全的map
synchronizedSet(Set set); // 返回线程安全的set;
Collections 还可以设置不可变集合:
-
emptyXxx()
: 返回一个空的、不可变的集合对象。 -
singletonXxx()
:返回一个只包含指定对象的不可变集合。 -
unmodifiableXxx()
:返回指定集合对象的不可变视图。
// Collection.emptyXxx(); 创建一个空的、不可改变的Xxx对象
List
Arrays 类的常见操作
- 1.排序
sort()
和parallelSort()
int a[] = { 1, 3, 2, 7, 6, 5, 4, 9 };
Arrays.sort(a); // sort(int[] a)方法按照数字顺序排列指定的数组。
Arrays.sort(a, 2, 6); // sort(int[] a,int fromIndex,int toIndex)按升序排列数组的指定范围
Arrays.parallelSort(c); // parallelSort(int[] a) 按照数字顺序排列指定的数组(并行的)。同sort方法一样也有按范围的排序
char d[] = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' };
Arrays.parallelSort(d); // parallelSort给字符数组排序,sort也可以
String[] strs = { "abcdehg", "abcdefg", "abcdeag" };
Arrays.sort(strs); // parallelSort给字符串排序,sort也可以
System.out.println(Arrays.toString(strs));//[abcdeag, abcdefg, abcdehg]
- 2.查找
binarySearch()
char[] e = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' };
Arrays.sort(e); // 排序后再进行二分查找,否则找不到
int s = Arrays.binarySearch(e, 'c');
System.out.println("字符c在数组的位置:" + s);
- 3.比较
equals()
char[] e = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' };
char[] f = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' };
Arrays.equals(e, f) // true
- 4.填充
fill()
int[] g = { 1, 2, 3, 3, 3, 3, 6, 6, 6 };
Arrays.fill(g, 3); //333333333
int[] h = { 1, 2, 3, 3, 3, 3, 6, 6, 6, };
Arrays.fill(h, 0, 2, 9); // 993333666 数组中指定范围元素重新分配值
- 5.转列表
asList()
List stooges = Arrays.asList("Larry", "Moe", "Curly");
- 6.转字符串
toString()
char[] k = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' };
System.out.println(Arrays.toString(k));// [a, f, b, c, e, A, C, B]
- 7.复制
copyOf()
int[] h = { 1, 2, 3, 3, 3, 3, 6, 6, 6, };
int i[] = Arrays.copyOf(h, 6); //123333 copyOf 方法实现数组复制,h为数组,6为复制的长度
// copyOfRange将指定数组的指定范围复制到新数组中
int j[] = Arrays.copyOfRange(h, 6, 11); //结果66600(h数组只有9个元素这里是从索引6到索引11复制所以不足的就为0)
38.深拷贝 VS 浅拷贝
- 深拷贝:基本类型进行值传递,对引用类型,创建一个新的对象,并复制其内容。
-
浅拷贝:基本类型进行值传递,对引用类型,进行引用传递的拷贝。
39. 补充: 抽象类 VS 接口
参考:深入理解Java的接口和抽象类
1 抽象方法
抽象方法是一种页数的方法,它只有声明,没有具体的实现。
abstract void fun()
2 抽象类
如果一个类含有抽象方法,就称这个类是抽象类。抽象类在类前用 abstract 关键字修饰。
[public] abstract class ClassName {
abstract void fun();
}
抽象类就是为了继承而存在的。
包含抽象方法的类成为抽象类,但抽象类中也可以有非抽象方法。抽象类和普通方法一样,可以拥有成员变量和普通的成员方法。
抽象类和普通类的区别:
-
- 抽象类必须为public 或 protected,默认是public(为了继承)
-
- 抽象类不能用来实例化,也就是不能用来创建对象。
- 3.如果一个类继承了抽象类,则子类必须实现父类的抽象方法。
除了这3点,其他和普通类没有区别。
3. 接口
接口泛指提供给别人调用的方法或函数,接口是对行为的抽象。
[public] interface InterfaceName {
}
接口中可以含有变量和方法,但是接口中的变量和方法会被隐式的指定
- 接口变量:
public static final
- 接口方法:
public abstract
接口中不能有具体的实现,接口中的方法都必须是抽象方法。
所以,可以把接口认为是一种极度抽象的类型。
4. 抽象类和接口的区别
- 1.抽象类是对一种事物的抽象,是对类的整体(属性+行为)的抽象
- 2.接口是对行为的抽象。
类比:飞机和鸟是不同的事物,但有一个共性,都会飞行。
所以可以将飞机设计为一个类Airplane,将鸟设计为一个类Bird。但是不能将飞行也设计为类。可以将飞行设计为一个接口Fly,里面包含了飞行的方法fly().
不同类型的飞机和鸟直接继承Airplane 和 Bird类即可,然后根据需要去实现Fly 接口。
也就是说,一个类继承了抽象类,则子类必须是抽象类的种类。而接口实现表示的是有没有,具不具备这种行为。(能飞,则可以实现Fly接口,不能飞则不行)
设计层面,抽象类是作为很多子类的父类,是一种模板设计。
接口是一种行为规范,是一种辐射式设计。
对于抽象类,如果添加了新的方法,子类可以不进行变更。
如果接口进行了变更,则实现这个接口的类都必须进行相应的改动。