共计 14471 个字符,预计需要花费 37 分钟才能阅读完成。
前言
因为博主是 2021 届的毕业生,为了筹备秋招,特意总结的 Java 基础知识面试高频题,最初也算找到了挺称心的工作。因而回馈给大家,心愿能对大家起到肯定的帮忙。
0. 入门常识
0.1 Java 特点
- 简略易学
- 面向对象(封装、继承、多态)
- 平台独立
- 安全可靠
- 反对多线程
- 解释和编译共存
- 安全性
- 健壮性(Java 语言的强类型机制、异样解决、垃圾的主动收集等)
- …
0.2 Java 和 C++
- 相同点:两者均为 OOP 语言,均反对 OOP 的三大个性(封装、继承、多态);
-
不同点:
- Java 不存在指针的概念,所以内存更加平安;
- Java 类是单继承(然而接口能够多继承),C++ 的类是多继承;
- Java 中有主动内存管理机制,然而 C++ 中须要开发者手动开释内存;
- C/C++ 中,字符串和字符数组最初均有一个额定的
\0
标记来示意完结,但 Java 中不存在这一概念;
0.3 JRE 和 JDK
- JRE(Java Runtime Environment),即 Java 运行时环境,是用来运行曾经编译过的 Java 程序所需内容的汇合(JVM、Java 类库、Java 命令等),不能用来开发新程序;
- JDK(Java Development Kit),即 Java 开发工具包,是功能齐全的 Java SDK,蕴含 JRE 领有的所有,还有编译器和其余工具,如果咱们想要创立和编译新程序,就必须应用到它;
0.4 Java 程序编译过程
咱们编译的源代码(xxx.java
)经 JDK 中的 javac
命令编译后,成为 JVM 可能了解的 Java 字节码(xxx.class
),而后经由 JVM 加载,通过解释器 逐行解释执行,这就是为什么能常常听见说 Java 是一门编译和解释共存的语言。
其中 JVM 是解释 Java 字节码(xxx.class
)的虚拟机,针对不同零碎均有特定实现,不便一次编译,屡次运行,即 Java 语言的平台独立性;
1. 数据类型
1.1 根本数据类型
数据类型 | bit | 字节 | 封装类 | 数据范畴 | 默认值 |
---|---|---|---|---|---|
byte |
8 | 1 | Byte |
$-2^7$ ~ $2^7-1$ | 0 |
short |
16 | 2 | Short |
$-2^{15}$ ~ $2^{15}-1$ | 0 |
char |
16 | 2 | Character |
\u0000 ~ \uffff ($0$ ~ $65535$) |
u0000 |
int |
32 | 4 | Integer |
$-2^{31}$ ~ $2^{31}-1$ | 0 |
long |
64 | 8 | Long |
$-2^{63}$ ~ $2^{63}-1$ | 0L |
float |
32 | 4 | Float |
$3.4e^{-45}$ ~ $1.4e^{38}$ | 0.0f |
double |
64 | 8 | Double |
$4.9e^{-324}$ ~ $1.8e^{308}$ | 0.0D |
boolean |
不确定 | 不确定 | Boolean |
true 或 false |
false |
留神:
boolean
个别用 1bit
来存储,然而具体大小并未规定,JVM 在编译期将boolean
类型转换为int
,此时 1 代表true
,0
代表false
。此外,JVM 还指出boolean
数组,但底层是通过byte
数组来实现;- 应用
long
类型时,须要在后边加上L
,否则将其作为整型解析,可能会导致越界; - 浮点数如果没有明确指定
float
还是double
,对立按double
解决; char
是用 单引号‘’
将内容括起来,相当于一个整型值(ASCII 值),可能加入表达式运算;而String
是用 双引号“”
将内容括起来,代表的是一个地址值;
1.2 援用类型
数据类型 | 默认值 |
---|---|
数组 | null |
类 | null |
接口 | null |
1.3 封装类
根本数据类型都有其对应的封装类,两者之间的赋值通过 主动装箱 和 主动拆箱 来实现;
- 主动装箱:将根本数据类型装箱为封装类;
// 理论调用 Integer.valueOf(12)
Integer x = 12;
- 主动拆箱:将封装类拆箱为根本数据类型;
Integer x = 12;
// 理论调用 x.intValue()
int y = x;
-
根本类型与对应封装类的不同
- 根本类型只能按值传递,封装类按援用传递;
- 根本类型 会在 栈 中创立,效率较高,但可能存在内存泄露问题;封装类对象会在堆中创立 ,其 援用在栈中创立;
1.4 缓存池
以 new Integer(123)
和 Integer.valueOf(123)
为例:
- 通过
new
的形式每次都会创立一个新的对象; - 通过
valueOf()
的形式则会优先判断该值是否位于缓存池,如果在的话就间接返回缓存池中的内容,屡次调用指向同一个对象的援用;
Integer x = new Integer(123);
Integer y = new Integer(123);
// false,通过 new 的形式,每次都会创立一个新对象,指向不同对象
System.out.println(x == y);
Integer m = Integer.valueOf(123);
Integer n = Integer.valueOf(123);
// true,通过 valueOf()的形式,先到缓存池中查找,存在时则屡次调用也是指向同一对象
System.out.println(m == n);
数据类型 | 默认缓存池 |
---|---|
Byte |
$-2^7$ ~ $2^7-1$ |
Character |
\u0000 ~ \u007F |
Short |
$-2^7$ ~ $2^7-1$ |
Integer |
$-2^7$ ~ $2^7-1$ |
Boolean |
true & false |
2. 字符串 String
2.1 定义
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];}
上述代码为 Java 8 中 String 的定义,其底层实际上应用的是字符(char
)数组,而且因为被申明为 final
,代表着它 不能被继承。而且一旦初始化之后就不能再去援用其余数组,这样就保障了 String
的不可变性,也因而 String 是线程平安的。
2.2 不可变性的长处
- 用于缓存
hash
值
因为 String
的 hash
值被频繁应用,它的不可变性使得 hash
值也不可变,此时只须要进行一次计算;
- 字符串常量池(String Pool)的须要
如果一个 String
对象曾经被创立过,那么就会优先从字符串常量池中获取其援用,其不可变性确保了不同援用指向同一 String
对象;
- 安全性
咱们常常用 String
作为咱们办法的参数,其不变性可能保障参数不可变;
- 线程平安
String
的不可变性让它天生 具备线程平安,可能在多个线程中方便使用而不必思考线程平安问题。
2.3 String vs StringBuffer vs StringBuffer
次要从三个方面对三者进行比照:
可变性 | 线程平安 | 实用场景 | |
---|---|---|---|
String |
不可变 | 平安 | 操作大量的数据 |
StringBuffer |
可变 | 平安,外部应用 synchronized 进行同步 |
多线程操作字符串缓冲区下操作大量数据 |
StringBuilder |
可变 | 不平安 | 单线程操作字符串缓冲区下操作大量数据,性能高于 StringBuffer |
2.4 字符串常量池(String Pool)
String Pool 位于 办法区,通常保留着所有 字符串字面量(literal strings),在编译期间就被确定。此外,还能够用 String
中的 intern()
办法在运行过程中增加到 String Pool 中。当一个字符串调用 intern()
时,如果 String Pool 中曾经存在字面量雷同的字符串,则会返回 String Pool 中的援用;如果不存在,则向 String Pool 中增加一个新的字符串,同时返回新字符串的援用;
String s1 = new String("aaa");
String s2 = new String("aaa");
// false 两个字符串指向不同对象
System.out.println(s1 == s2);
String s3 = s1.intern();
String s4 = s1.intern();
// true,常量池中存在字面量雷同的字符串,间接取出
System.out.println(s3 == s4);
在上面的代码中,内存剖析如下图:
String str1 = "村雨遥";
String str2 = "村雨遥";
String str3 = new String("村雨遥");
String str4 = new String("村雨遥");
// true,两个援用指向常量池中的同一对象
System.out.println(str1 == str2);
// false,两个援用指向堆中不同对象
System.out.println(str3 == str4);
2.5 new String(“xxx”)
应用 new
的形式创立字符串对象,会有两种不同的状况:
- String Pool 中不存在“xxx”
此时会创立两个字符串对象,“xxx”属于字符串字面量,因而在编译期会在 String Pool 中创立一个字符串对象,用于指向该字符串的字面量“xxx”;而后 new
会在堆中创立一个字符串对象;
- String Pool 中存在“xxx”
此时只须要创立一个字符串对象,因为 String Pool 中曾经存在指向“xxx”的对象,所以间接在堆中创立一个字符串对象;
3. 根底语法
3.1 正文
- 单行正文
// 这是单行正文
String name = "村雨遥";
- 多行正文
/*
* 这是多行正文
* name,公众号
*/
String name = "村雨遥";
- 文档正文
/**
* @author : 村雨遥
* @param : name,公众号
*/
String name = "村雨遥";
3.2 常见关键字
3.3 标识符和关键字
- 标识符:用于给程序、类、对象、变量、办法、接口、自定义数据类型等命名;
- 关键字:非凡的标识符,被 Java 赋予了非凡含意,只能有特定用处;
-
标识符命名规定 (能够参考《阿里巴巴开发手册》,关注公众号【 村雨遥】回复【资源下载】下载 PDF)
- 标识符由英文字符大小写(a – z, A – Z)、数字(0 – 9)、下划线(
_
)和美元符号($
)组成; - 不能以数字结尾,不能是关键字;
- 严格辨别大小写;
- 包名:多个单词组成是所有单词均小写;
- 类名和接口:大写驼峰命名法;
- 变量名和函数名:多个单词组成时,第一个单词全小写,其余单词采纳大写驼峰命名法;
- 常量名:字母全副大写,单词之间用下划线(
_
)宰割;
- 标识符由英文字符大小写(a – z, A – Z)、数字(0 – 9)、下划线(
3.4 拜访控制符
作用域 | 以后类 | 同一 package 的类 |
子类 | 其余 package 的类 |
---|---|---|---|---|
public |
😀 | 😀 | 😀 | 😀 |
protected |
😀 | 😀 | 😀 | 😡 |
default |
😀 | 😀 | 😡 | 😡 |
private |
😀 | 😡 | 😡 | 😡 |
3.5 static、final、this、super
- static
static
次要有如下 4 中应用场景:
- 润饰成员变量和成员办法 :被
static
润饰的成员属于类,属于动态成员变量,存储在 Java 内存中的 办法区 ,不属于单个对象,被所有对象共享,而且最好通过类名. 动态成员名 / 静态方法名()
调用; - 动态代码块 :定义在类中办法外,先于非动态代码块之前执行( 动态代码块 -> 非动态代码块 -> 构造方法),而且不论执行多少次创立新对象的操作,动态代码只执行一次;
- 动态外部类 :
static
要润饰类时,只有润饰外部类这一种用法。 非动态外部类在编译后会隐含保留一个援用,用于指向创立它的外部类,然而动态外部类不存在。即 外部类的创立不必依赖外围类的创立,同时外部类也只能应用任意外部类的static
成员变量和办法; - 动态导包:用于导入动态资源,
import static
用于指定导入某一类中的动态资源,而后咱们就能够间接应用类中的动态成员变量和办法; -
留神:
abstract
办法不能同时是static
的,因为abstract
办法须要被重写,但static
办法不能够;- 不能从
static
办法外部收回对非静态方法的调用,因为静态方法只能拜访动态成员,而非静态方法的调用须要先创建对象; static
不能用于润饰局部变量;- 外部类与动态外部类的区别:动态外部类绝对外部类是独立存在的,在动态外部类中无奈间接拜访外部类中变量和办法。如果要进行拜访,则必须
new
一个外部类对象,应用该对象来进行拜访,但对于动态变量和静态方法,可能间接调用。而一般的外部类作为外部类的一个成员而存在,可能间接拜访外部类属性,调用外部类办法。
- final
- 润饰类时,被润饰的类不能被继承,而且类中所有成员办法均被隐式指定为
final
办法; - 润饰办法时,表明该办法无奈被重写;
- 润饰变量时,阐明该 变量是一个常量。若变量为根本数据类型,则一旦初始化后不能再扭转;若变量是援用类型,则初始化后不能指向其余对象;
- this
用于援用类的以后实例,比方咱们最罕用的构造方法中,留神不能用在 static
办法中;
public class User{
int age;
public User(int age){this.age = age;}
}
其中 this.age
阐明拜访的是 User
类中的成员变量,而前面的 age
则代表传入的形参;
- super
用于从子类拜访父类中的变量和办法,留神不能用在 static
办法中;
public class Father{
String name;
public Father(String name){this.name = name;}
public Father(){}
}
public class Son extends Father{public Son(String name){super();
this.name = name + ".jr";
}
}
3.6 continue、break 和 return
关键字 | 阐明 |
---|---|
continue |
用于循环构造,指跳出以后循环,进入下一次循环 |
break |
用于循环构造,指跳出整个循环体,继续执行循环上面的语句 |
return |
1. return ; :间接用 return 完结办法执行,用于没有返回值函数的办法;2. return value; :return 一个特定值,用于有返回值函数的办法 |
3.7 while 循环与 do 循环
while
循环构造在循环开始前会判断下一个迭代是否应该持续,可能一次循环体都不执行;
do……while
会在循环的后果来判断是否持续下一轮迭代,至多会执行一次循环体;
3.8 final、finally、finalize
- final
final
既是一个修饰符,也是一个关键字,润饰不同对象时,示意的意义也不一样;
- 润饰类: 示意该类无奈被继承;
- 润饰变量:若变量是根本数据类型,则其数值一旦初始化后就不能再扭转,若变量是援用类型,则在其初始化之后便不能再让其指向另一个对象,但其指向的对象的内容是可变的;
- 润饰办法:示意办法无奈被重写,然而容许重载,
private
办法会隐式指定为final
办法;
- finally
finally
是一个关键字,在异样解决时提供finally
块来执行任何革除操作,无论是否有异样被抛出或捕捉,finally
块均会被执行,通常用于开释资源;-
finally
失常状况下肯定会被执行,然而在如下两种状况下不会执行:- 对应的
try
未执行,则该try
块的finally
块并不会被执行; - 若
try
块中 JVM 关机,则finally
块也不会执行;
- 对应的
finally
中如果有return
语句,则会笼罩try
或catch
中的return
语句,导致两者无奈return
,所以倡议finally
中不要存在return
关键字;
- finallize
finallize()
是 Object
类的 protected
办法,子类可能笼罩该办法以实现资源清理工作;
GC 在回收前均会调用该办法,然而 finalize()
办法存在如下问题:
- Java 语言标准不保障
finalize()
办法会被及时执行,也不保障他们肯定被执行; finalize()
办法会带来性能问题,因为 JVM 通常在独自的低优先线程中实现finalize
的执行;finalize()
办法中,可将待回收对象赋值给GC Roots
可达的对象援用,从而达到对象再生的目标;finalize()
办法最多由 GC 执行一次(然而能够手动调用对象的finalize
办法);
4. 运算符
4.1 算术运算
操作符 | 形容 | 例子 |
---|---|---|
+ |
加法 – 相加运算符两侧的值 | A + B 等于 30 |
- |
减法 – 左操作数减去右操作数 | A – B 等于 -10 |
* |
乘法 – 相乘操作符两侧的值 | A * B 等于 200 |
/ |
除法 – 左操作数除以右操作数 | B / A 等于 2 |
% |
取余 – 左操作数除以右操作数的余数 | B%A 等于 0 |
++ |
自增: 操作数的值减少 1 | B++ 或 ++B 等于 21 |
-- |
自减: 操作数的值缩小 1 | B– 或 –B 等于 19 |
留神 :++
和 --
能够放在操作数之前,也能够放在操作数之后; 位于操作数之前时,先自增 / 减,再赋值;位于操作数之后,先赋值,再自增 / 减 ;总结起来就是 符号在前就先加 / 减,符号在后就后加 / 减。
4.2 关系运算符
运算符 | 形容 | 例子 |
---|---|---|
== |
查看如果两个操作数的值是否相等,如果相等则条件为真。 | (A == B)为假。 |
!= |
查看如果两个操作数的值是否相等,如果值不相等则条件为真。 | (A != B) 为真。 |
> |
查看左操作数的值是否大于右操作数的值,如果是那么条件为真。 | (A> B)为假。 |
< |
查看左操作数的值是否小于右操作数的值,如果是那么条件为真。 | (A <B)为真。 |
>= |
查看左操作数的值是否大于或等于右操作数的值,如果是那么条件为真。 | (A> = B)为假。 |
<= |
查看左操作数的值是否小于或等于右操作数的值,如果是那么条件为真。 | (A <= B)为真。 |
4.3 位运算符
操作符 | 形容 | 例子 | |
---|---|---|---|
& |
如果绝对应位都是 1,则后果为 1,否则为 0 | (A&B),失去 12,即 0000 1100 | |
` | ` | 如果绝对应位都是 0,则后果为 0,否则为 1 | 如果绝对应位都是 0,则后果为 0,否则为 1 |
^ |
如果绝对应位值雷同,则后果为 0,否则为 1 | (A ^ B)失去 49,即 0011 0001 | |
〜 |
按位取反运算符翻转操作数的每一位,即 0 变成 1,1 变成 0。 | (〜A)失去 -61,即 1100 0011 | |
<< |
按位左移运算符。左操作数按位左移右操作数指定的位数。 | A << 2 失去 240,即 1111 0000 | |
>> |
按位右移运算符。左操作数按位右移右操作数指定的位数。 | A >> 2 失去 15 即 1111 | |
>>> |
按位右移补零操作符。左操作数的值按右操作数指定的位数右移,挪动失去的空位以零填充。 | A>>>2 失去 15 即 0000 1111 |
4.4 逻辑运算符
操作符 | 形容 | 例子 | ||||
---|---|---|---|---|---|---|
&& |
称为逻辑与运算符。当且仅当两个操作数都为真,条件才为真。 | (A && B) 为假。 |
||||
` | ` | 称为逻辑或操作符。如果任何两个操作数任何一个为真,条件为真。 | `(A | B)` 为真。 | ||
! |
称为逻辑非运算符。用来反转操作数的逻辑状态。如果条件为 true,则逻辑非运算符将失去 false。 | !(A && B) 为真。 |
4.5 赋值运算符
操作符 | 形容 | 例子 | |||
---|---|---|---|---|---|
= |
简略的赋值运算符,将右操作数的值赋给左侧操作数 | C = A + B 将把 A + B 失去的值赋给 C | |||
+= |
加和赋值操作符,它把左操作数和右操作数相加赋值给左操作数 | C + = A 等价于 C = C + A | |||
-= |
减和赋值操作符,它把左操作数和右操作数相减赋值给左操作数 | C – = A 等价于 C = C – A | |||
*= |
乘和赋值操作符,它把左操作数和右操作数相乘赋值给左操作数 | C = A 等价于 C = C A | |||
/= |
除和赋值操作符,它把左操作数和右操作数相除赋值给左操作数 | C / = A,C 与 A 同类型时等价于 C = C / A | |||
%= |
取模和赋值操作符,它把左操作数和右操作数取模后赋值给左操作数 | C%= A 等价于 C = C%A | |||
<< = |
左移位赋值运算符 | C << = 2 等价于 C = C << 2 | |||
>>= |
右移位赋值运算符 | C >> = 2 等价于 C = C >> 2 | |||
&= |
按位与赋值运算符 | C&= 2 等价于 C = C&2 | |||
^= |
按位异或赋值操作符 | C ^ = 2 等价于 C = C ^ 2 | |||
` | =` | 按位或赋值操作符 | C \ | = 2 等价于 C = C \ | 2 |
4.6 条件运算符(? :)
也叫作三元运算符,共有 3 个操作数,且须要判断布尔表达式的值;
variable x = (expression) ? value if true : value if false
4.7 instanceof
用于操作对象实例,查看该对象是否是一个特定类型(类类型或接口类型);
(Object reference variable) instanceof (class/interface type)
4.8 equals() 和 ==
==
根本数据类型用 ==
比拟的是值,用于援用数据类型时判断两个对象的内存地址是否相等,即两对象是否是同一个对象;
实质来讲,因为 Java 中只有值传递,所以不论是根本数据类型还是援用数据类型,比拟的其实都是值,只不过援用类型变量存的值是对象的地址;
equals()
作用也是判断两个对象是否相等,然而 不能用于根本数据类型变量的比拟。存在于 Object()
类中,所以所有类都具备 equals()
办法存在两种应用状况:
- 类未笼罩
equals()
办法:此时通过equals()
比拟该类的两个对象时,等价于==
比拟这两个对象,默认应用Object
类中的equals()
办法; - 类笼罩了
equals()
办法:一旦笼罩了该办法,则用来比拟两个对象的内容是否相等,如咱们罕用的String、BitSet、Data、File
就笼罩了equals()
办法;
5. 办法
5.1 办法的类型
- 无参无返回值;
- 无参有返回值;
- 有参无返回值;
- 有参有返回值;
5.2 重载和重写
- 重载(Overload)
重载就是同样办法可能依据输出的不同,做出不同的解决。重载产生在 编译期 ,而且在同一个类中, 办法名必须雷同,参数类型、参数个数、参数程序不同,返回值和拜访修饰符能够不同。 总的而言:重载就是同一类中多个同名办法依据不同传参来执行不同的逻辑解决。
- 重写(Override)
重写是当子类继承自父类的雷同办法,输出数据一样,但最终响应不同于父类。重写产生在 运行期 ,是子类对父类容许拜访的办法的实现逻辑进行改写。重写办法的办法名、参数列表以及返回值必须雷同,抛出的异样范畴不超出父类,拜访修饰符的范畴也不能小于父类。此外,若父类办法别 private/final/static
润饰,则子类无奈重写父类办法,但 static
润饰的办法能被再次申明。 构造方法是个特例,不能被重写 。总结起来就是: 重写即子类对父类办法的革新,内部样子不能扭转,但可能扭转外部逻辑。
- 重载 vs 重写
不同点 | 重载 | 重写 |
---|---|---|
参数列表 | 必须不同 | 必须雷同 |
返回类型 | 可不同 | 必须雷同 |
拜访修饰符 | 可不同 | 不能比父类更严格 |
产生范畴 | 同一类中 | 父子类 |
异样范畴 | 可批改 | 能够缩小或删除,不能抛新异样或范畴更广的异样 |
产生阶段 | 编译期 | 运行期 |
5.3 深 / 浅拷贝
- 浅拷贝
浅拷贝是 按位拷贝对象,会创立一个新对象,该对象具备原始对象属性值的准确拷贝。 若属性是根本类型,则拷贝的是根本类型的值;若属性是援用类型(内存地址),则拷贝的是内存地址。因而,一旦其中任一对象扭转了该援用类型属性,均会影响到对方;
- 深拷贝
深拷贝会 拷贝所有属性,同时拷贝属性指向的动态分配的内存 。当对象和它援用的对象一起拷贝是即产生深拷贝, 相比于浅拷贝,深拷贝速度较慢同时花销更大。
- 总结
浅拷贝后,扭转其中任一份值都会引起另一份值的扭转;而深拷贝后,扭转其中任何一份值,均不会对另一份值造成影响;
5.4 值传递
举荐浏览:https://juejin.im/post/5bce68…
5.4.1 形参和实参
- 形参:办法被调用时须要传递进来的参数,如
func(String name)
中的name
就是一个形参,只有在func
被调用时name
才被分配内存空间,当办法执行完后,name
将主动销毁开释空间; - 实参:办法调用时传入的理论值,在办法调用前就曾经被初始化且在办法调用时被传入;
public static void func(String name){System.out.println(name);
}
public static void main(String[] args) {
// 实参
String name = "村雨遥";
func(name);
}
5.4.2 值传递和援用传递
- 值传递
办法被调用时,实参通过形参将其内容正本传入办法外部,此时形参接管的内容实际上是实参的一个拷贝,因而在办法内对形参的任何操作均只针对于实参的拷贝,不会影响到实参原始值的内容。即 值传递的是实参的一个正本,对正本的操作不会影响实参原始值,也即无论形参如何变动,都不会影响到实参的内容。
public static void valueCrossTest(int age,float weight){System.out.println("传入的 age:"+age);
System.out.println("传入的 weight:"+weight);
age=33;
weight=89.5f;
System.out.println("办法内从新赋值后的 age:"+age);
System.out.println("办法内从新赋值后的 weight:"+weight);
}
public static void main(String[] args) {
int a=25;
float w=77.5f;
valueCrossTest(a,w);
// a = 25,原始值不收影响
System.out.println("办法执行后的 age:"+a);
// w = 77.5,原始值不收影响
System.out.println("办法执行后的 weight:"+w)
}
- 援用传递
援用即指向实在内容的地址值,在办法调用时,实参的地址被传递给相应形参,在办法体内,形参和实参指向同一个地址内存,因而此时操作形参也会影响到实参的实在内容。
但 Java 中并 不存在援用传递 ,因为 无论是根本类型还是援用类型,在实参传入形参时,均为值传递,即传递的都是一个正本,而非实参内容自身。
- 总结
如果是对根本数据类型的数据进行操作,因为实参原始内容和正本都是存储理论值,并且处于不同栈区,因而对形参的操作,实参原始内容不受影响。
如果是对援用类型的数据进行操作,分两种状况,
- 一种是形参和实参放弃指向同一个对象地址,则形参的操作,会影响实参指向的对象的内容。
public static void PersonCrossTest(Person person){System.out.println("传入的 person 的 name:" + person.getName());
person.setName("我是张小龙");
System.out.println("办法内从新赋值后的 name:" + person.getName());
}
- 另一种是形参被改变指向新的对象地址(如从新赋值援用),则形参的操作,不会影响实参指向的对象的内容。
public static void PersonCrossTest(Person person){System.out.println("传入的 person 的 name:" + person.getName());
person=new Person();
person.setName("我是张小龙");
System.out.println("办法内从新赋值后的 name:" + person.getName());
}
6. 面向对象
6.1 面向对象 vs 面向过程
举荐浏览:https://www.zhihu.com/questio…
- 面向对象(Object Oriented)
面向过程是一种 对事实世界了解和形象的办法 , 更容易保护、复用、扩大 。最次要的特点就是 继承、封装、多态,所以 设计出的零碎耦合性较低,但比起面向过程性能要低。
- 面向过程(Procedure Oriented)
面向过程是一种 以过程为核心 的编程思维,以正在产生为次要指标进行编程,不同于面向的的是谁受影响。最次要的不同就在于 封装、继承、多态,其性能比面向对象更高。
- 总结
面向对象的形式使得每个类都各司其职,最初整合到一起来共同完成一个我的项目,而面向过程则是让一个类中的性能越来越多,就像一个全栈工程师可能一个人搞定所有事。
6.2 封装、继承、多态
- 封装
将客观事物封装为形象的类,同时类能把本人的数据和办法只让可信的类或对象进行操作,对不可信的类进行信息暗藏。即把属于同一类事物的共性(属性与办法)归到一个类,从而方便使用。
通过 封装,实现了 专业分工 ,将能实现特定性能的代码封装为独立实体,供咱们在须要时调用。此外,封装还 暗藏了信息以及实现细节,使得咱们通过拜访权限权限符就能将想要暗藏的信息暗藏起来。
- 继承
能够应用现有类的所有性能,且无需重写现有类来进行性能扩大,即共性对共性的属性与办法的承受,并退出个性所特有的属性与办法。通过继承的新类叫做 子类 / 派生类 ,被继承的类叫做 父类 / 基类 / 超类,具备如下特点:
- 子类领有父类对象所有属性和办法,但父类中的公有属性和办法,子类是无法访问的;
- 子类能够对父类进行扩大;
- 子类能够用本人的形式来实现父类的办法;
- 多态
多态是容许 将父对象设置为和一个或多个其子对象相等的技术 ,赋值后,父对象可能依据指向的子类对象的个性以不同形式运作,即 父类援用指向子类对象实例 ,有 重载和重写 两种实现形式。具备如下特点:
- 对象类型不可变,但援用类型可变;
- 对象类型和援用类型之间有继承(类)/ 实现(接口)的关系;
- 办法具备多态性,但属性不具备;
- 若子类重写了父类办法,则真正执行的是子类笼罩的办法,若子类未笼罩父类办法,则调用父类的办法。
6.3 成员变量 vs 局部变量 vs 动态变量
不同 | 语法 | 存储地位 | 生命周期 | 初始化值 | 调用形式 | 别名 |
---|---|---|---|---|---|---|
成员变量 | 1、属于类 2、能被拜访控制符、 static、final 等润饰 |
堆 | 与对象共存亡 | 有,根本数据类型为对应默认值,而对象对立为 null |
对象调用 | 实例变量 |
局部变量 | 1、属于办法(办法中的变量或参数) 2、不能被拜访控制符及 static 润饰,但能够被 final 润饰 |
栈 | 与办法共存亡 | 无,必须定义赋值后应用 | ||
动态变量 | 1、属于类 2、被 static 润饰,被所有类对象共用 |
办法区 | 与类共存亡 | 同成员变量初始化值 | 类名调用(举荐)、对象调用 | 类变量 |
6.4 构造方法的特点
- 办法名与类名同名;
- 无返回值,但不能用
void
关键字申明; - 生成类对象时主动执行,无需显式调用;
6.5 抽象类 & 接口
- 接口
- 接口中所有办法默认是
public
,而且不能有实现(Java 8 之前,Java 8 开始能够有默认实现); - 接口中所有变量均为
static、final
,不能有其余变量; - 一个类能够实现多个接口(通过
implements
关键字),而且接口本身能够通过extends
来扩大多个接口; - 接口是对行为的形象,属于行为规范;
- 抽象类
- 抽象类中既能够有形象办法,也能够有非形象的办法;
- 一个类只能实现一个抽象类;
- 形象办法能够被
public、protected、default
润饰,但不能用private
,否则不能被重写; - 形象是对类的形象,是一种模板设计;
6.6 Object 类中常见办法
办法 | 阐明 |
---|---|
public final native Class<?> getClass() |
用于返回以后运行时对象的 Class 对象,应用了 final 关键字润饰,故不容许子类重写 |
public native int hashCode() |
用于返回对象的哈希码,次要应用在哈希表中,比方 JDK 中的 HashMap |
public boolean equals(Object obj) |
用于比拟 2 个对象的内存地址是否相等,String 类对该办法进行了重写用户比拟字符串的值是否相等 |
protected native Object clone() throws CloneNotSupportedException |
用于创立并返回以后对象的一份浅拷贝。个别状况下,对于任何对象 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() |
不能重写。唤醒一个在此对象监视器上期待的线程(监视器相当于就是锁的概念)。如果有多个线程在期待只会任意唤醒一个 |
public final native void notifyAll() |
不能重写。跟 notify 一样,惟一的区别就是会唤醒在此对象监视器上期待的所有线程,而不是一个线程 |
public final native void wait(long timeout) throws InterruptedException |
不能重写。暂停线程的执行留神:sleep 办法没有开释锁,而 wait 办法开释了锁。timeout 是等待时间,调用该办法后以后线程进入睡眠状态,晓得如下工夫产生: 1. 其余线程调用该对象的 notify()/notifyAll() 办法;2. 工夫距离到了; 3. 其余线程调用了 interrupt() 中断该线程; |
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 {} |
实例被垃圾回收器回收的时候触发的操作 |
6.7 hashCode & equals
举荐浏览:https://juejin.im/post/5a4379…
6.7.1 equals
- 重写
equals()
办法的准则:
准则 | 阐明 |
---|---|
自反性 | 对任意非空援用值 x ,x.equals(x) 应该返回 true |
对称性 | 对于任何非空援用值 x 和 y ,当 y.equals(x) 返回 true 时,x.equals(y) 也应返回 true |
传递性 | 对于任何非空援用值 x、y 和 z ,如果 x.equals(y) 返回 true ,并且 y.equals(z) 返回 true ,那么 x.equals(z) 也应返回 true |
一致性 | 对于任何非空援用值 x 和 y ,屡次调用 x.equals(y) 始终返回 true 或始终返回 false ,前提是对象上 equals 比拟中所用的信息没有被批改 |
非空性 | 对于任何非空援用值 x ,x.equals(null) 都应返回 false |
6.7.2 hashCode
hashCode
用于返回对象 hash
值,次要是为了放慢查找的快捷性,因为 hashCode()
是 Object
类中的办法,所以所有 Java 类均有 hashCode()
,在 HashTable
和 HashMap
这类的散列构造中,均是通过 hashCode()
来查找在散列表中地位,通过 hashCode
可能较快的茶道小内存块。
6.7.3 为什么重写 equals()
必须重写 hashCode()
- 若两个对象相等,则
hashCode()
肯定也雷同,因为equals()
是相对牢靠的; - 两个对象相等,则两个对象别离调用
equals()
办法也返回true
; - 两个对象有雷同的
hashCode()
,他们不肯定相等,因为hashCode()
不是相对牢靠的; - 如果重写了
equals()
,但保留hashCode()
的实现不变,则可能呈现两者相等,但hashCode
却不一样; - 因而,一旦重写了
equals()
办法,则必须重写hashCode()
,hashCode()
的默认行为是对堆上的对象产生独特值。如果没有重写hashCode()
,则该class
的两个对象无论如何都不会相等(即便这两个对象指向雷同的数据)。
6.8 序列化与反序列化
6.8.1 定义
- 序列化:指将对象转换为字节序列的过程;
- 反序列化:指将字节序列转换为指标对象的过程;
6.8.2 须要序列化的场景
当 Java 对象须要在网络上传输或者长久化存储到文件中时,咱们就须要对象进行序列化;
6.8.3 如何实现序列化
要实现序列化,只须要让类实现 Serializable
接口即可,此时就标注该类对象可能被序列化;
针对类中某些数据不想序列化时,能够应用 transient
关键字来实现,例如:
// 通过关键字 transient 润饰,表明不参加序列化
transient private String telephone;