javaSE 面向对象 复习

哑剧

java SE 复习整理 (仗剑走天涯)

面向对象

在java中万物皆对象

面向对象&面向过程

面向过程 –步骤化

面向过程就是分析出实现需求所需要的步骤,通过函数(方法)一步一步实现这些步骤,接着依次调用即可

面向对象 –行为化

面向对象是把整个需求按照特点、功能划分,将这些存在共性的部分封装成类(类实例化后才是对象),让对象去解决对应的问题

封装

将类的属性隐藏在内部、外部不能直接访问和修改

核心思想

尽可能的把属性都隐藏在内部、对外提供方法访问、在这些方法中添加逻辑处理来实现过滤以及屏蔽错误数据

一般步骤

修改属性的访问权限为私有、使外部无法直接访问

提供外部可以直接调用的方法

在该方法中加入逻辑控制、避免出现错误

成员变量&成员方法

成员变量

类的一些属性特征

成员方法

类的一些行为、动作

return关键字

终止当前方法继续执行

返回方法的返回值

在void中,即使没有返回值的方法中也能 用return

方法的重载

方法名必须一致

参数不一致,有两层含义第一是参数的数量不一致,第二层含义是:参数的类型不一致,【参数的名字一样不一样都行】

返回值无要求。

可变参数

语法:访问修饰符 返回类型 方法名(数据类型… 形参名) {}

这种参数可以随心所欲去传递参数

一个方法的形参列表最多只能出现一个可变参数

1
public int sum(int... nums,int... nums2);  // 不可以

可变参数可以和普通参数放在一起,但是可变参数必须放在最后

1
2
public int sum(int first,int... nums2);   // 可以
public int sum(int... nums2,int last); // 不可以

局部变量和作用域

成员变量会有默认值:基础数据都是零,char中的零是代表空字符,boolean是false,引用数据类型都是null

局部变量没有默认值:必须初始化才能使用

权限修饰符

√    √    √    √
√    √    √    ×
√    √    ×    ×
√    ×    ×    ×
作用域 当前类 同package 子孙类 其他package
public
protected ×
friendly( default ) × ×
private × × ×

class只能被public修饰,但是内部类可以被以上几种修饰

局部变量不能使用权限修饰符

构造方法(构造器)

方法名与类名相同

java的类默认带一个无参构造的构造方法

在new对象的时候主动被调用

如果自己手写了一个带参数的构造方法 那么这个带参数的构造方法会自动覆盖默认的那个构造方法 所以要手写一个不带参数的 要不然new的时候不能new不带参的

new一个对象干啥了

java在new一个对象的时候,会先查看对象所属的类有没有被加载到内存,如果没有的话,就会先通过类的权限定名来加载

加载并初始化类完成后,再进行对象的创建工作。

this关键字

每一个方法都会默认传入一个变量叫this,它永远指向调用它的【当前实例】

一个方法只有在调用的时候,才能明确方法中的【this】具体指向哪个实例对象。

我们可以使用this访问当前对象的方法和属性

如果我们使用无参构造会传入一个默认值,这就是典型的案例

1
2
3
public xiaobo(){
this("default");
}

String

引用数据类型

常用 new不new都行

1
2
3
String name = new String("xiaobo");

String name = "xiaobo";

使用string一定要注意,必须用一个新的String接受

1
String substring = name.substring(1, 3);

符串查找 indexOf()

String 类的 indexOf() 方法在字符串中查找子字符串出现的位置,如过存在返回字符串出现的位置(第一位为0),如果不存在返回 -1

字符串替换

java String 类的 replace 方法可以替换字符串中的字符

replace() 替换全部

replaceFirst() 替换第一个遇到的

replaceAll() 替换全部

字符串分割

split(string) 方法通过指定分隔符将字符串分割为数组

字符串截串

substring(string) 方法可以截取从第几个下标(0开始,包含第一个开始)到第几个下标(不包含)的字符串

字符串比较

equals() 区分大小写比较字符串

equalsIgnoreCase() 不区分大小写比较字符串

字符串小写转大写

String toUpperCase() 方法将字符串从小写转为大写

包装类和自动拆装箱

基本数据类型 包装类
byte Byte
short Short
int Integer
long Long
float Float
double Double
boolean Boolean
char Character

自动装箱:将基础数据类型自动装换为包装类

自动拆箱:将包装类自动转换为基础数据类型

继承

先这样讲 现在有人 但是人有男的 有女的 但是不管是男的还是女的都是人 所以男的和女的可以继承人该有的最基本的东西

继承可以解决代码复用的问题,一个类可以继承一个类,被继承的类我们称之为【父类】或者【超类】,另一个类称之为【子类】也叫【派生类】,子类可以通过extends关键字获取父类的成员变量和成员方法的能力,除了被private修饰的。在java中是单继承的,这样可以规范代码的实现

构造一个子类一定会先构造一个父类

super关键字

作用

在子类的成员方法中,访问父类的成员变量

在子类的成员方法中,访问父类的成员方法

在子类的构造方法中,访问父类的构造方法

super调用父类的方法和属性

先在当前类中寻找

当前类没有,继续向父类中寻找

如果还是没有,就向父类的父类继续寻找

直到到达一个所有类的共同父类,他叫Object

super构造器只能放在第一行

总结

子类继承了父类所有的非私有的属性和方法,可以直接调用。
子类在构造的时候,一定会构造一个父类,默认调用父类的无参构造器

子类如果希望指定去调用父类的某个构造器, 则显式的调用一下 : super(参数列表)

super和this当做构造器使用时, 必须放在构造器第一行,所以只能二选一

java 所有类都是 Object 类的子类, Object 是所有类的基类

子类最多只能继承一个父类(指直接继承), java 中是单继承机制,我们可以使用连续继承来实现

this&super

this super
访问方法 访问本实例的属性,没有会继续向父类检索 访问父类实例的属性,没有会继续向父类检索
访问属性 访问本实例的方法,没有会继续向父类检索 访问父类实例的方法,没有会继续向父类检索
访问构造方法 调用本类的构造器,必须放在第一行,不会向上检索 调用父类的构造器,必须放在第一行,不会向上检索

方法重写

重写一定要保证参数、名字全部一样

返回值要一样,或者返回父类的子类型

重载&重写

名称 范围 方法名 形参列表 返回类型 权限修饰
重载(overload) 本类 必须相同 类型,个数或者顺序不同,名字无所谓 没有要求 无要求
重写(override) 父子类 必须相同 必须相同 一样,或者子类的返回值是父类的返回值的子类 子类不能缩小父类的访问权限

final关键字

可以修饰变量,方法,以及类

作用

被final修饰的变量不能被修改,这里有两层含义,如果final修饰的是基础数据类型是只不能被修改,如果是引用数据类型就是引用指向不能被修改

被final修饰的方法不能被重写

被final修饰的类不能被继承

Object

方法 描述
boolean equals(Object obj) 指示某个其他对象是否与此对象“相等”
public native int hashCode(); 返回该对象的哈希码值
String toString() 返回该对象的字符串表示
Class<? extendsObject> getClass() 返回一个对象的运行时类
void notify() 唤醒在此对象监视器上等待的单个线程
void notifyAll() 唤醒在此对象监视器上等待的所有线程
void wait() 导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法
void wait(long timeout) 导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量
void wait(long timeout, int nanos) 导致当前的线程等待,直到其他线程调用此对象的 notify()
protected native Object clone() 克隆对象,浅拷贝
protected void finalize() 垃圾回收器准备释放内存的时候,会先调用finalize()

hashCode

一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值

hashCode特点

只能通过原文计算出hash值,而且每次计算都一样,不能通过hash值计算原文

原文的微小变化就能是hash值发生巨大变化

一个好的hash算法还能尽量避免发生hash值重复的情况,也叫hash碰撞

hashCode用途

保存密码

文件的校验,检查数据的一致性

常见的Hash摘要算法

Md5 英语:MD5 Message-Digest Algorithm,一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致

SHA1 英语:Secure Hash Algorithm 1,中文名:安全散列算法1,一种密码散列函数,SHA-1可以生成一个被称为消息摘要的160位(20字节)散列值,散列值通常的呈现形式为40个十六进制数

SHA256 SHA256算法使用的哈希值长度是256位

SHA512 算法使用的哈希值长度是512位

1
2
3
4
5
public static void main(String[] args) throws Exception {
MessageDigest sha512 = MessageDigest.getInstance("SHA-512");
byte[] digest = sha512.digest("123".getBytes());
System.out.println(digest.length);
}

使用相对应的算法只需要MessageDigest.getInstance(“SHA-512”);方法中改成相应的算法名即可

equals

源码

1
2
3
public boolean equals(Object obj) {
return (this == obj);
}

equals & == 的区别

==可以比基础数据类型也可以比较引用数据类型,比较基础数据类型时比较的是具体的值,比较引用数据类型实际上比较的是内存地址

equals是Object的一个方法,默认的实现就是 ==

我们可以重写equals方法,是我们的特性需求,比如String就重写了equals方法,所以字符串调用equals比较的是每一个字符

jdk 8 String重写的equals

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}

toString

1
2
3
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

对象名 + @ + hashCode

finalize()

java提供finalize()方法,垃圾回收器准备释放内存的时候,会先调用finalize()

jdk9开始声明已经过时

不推荐使用

clone()

克隆就是在内存里边赋值一个实例对象。但是Object的克隆方法只能浅拷贝。同时必须实现Cloneable接口

Object中默认的clone方法,是浅拷贝的

浅拷贝

浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象

实现对象拷贝的类,必须实现Cloneable接口,并覆写clone()方法。

student

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class Student implements  Cloneable{

private String name;

private Integer age;

public Student(String name,Integer age){
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

@Override
public String toString() {
return super.toString();
}

@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

main

1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) throws CloneNotSupportedException {

Student student = new Student("xiaobo",21);
Student clone = (Student) student.clone();

System.out.println(student);
System.out.println(clone);

System.out.println(student.getName().hashCode());
System.out.println(clone.getName().hashCode());
}

运行结果

1
2
3
4
com.dream.xiaobo.objects.Student@6d6f6e28
com.dream.xiaobo.objects.Student@135fbaa4
-759499924
-759499924

深拷贝

深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大

如果在拷贝一个对象时,要想让这个拷贝的对象和源对象完全彼此独立,那么在引用链上的每一级对象都要被显式的拷贝。所以创建彻底的深拷贝是非常麻烦的,尤其是在引用关系非常复杂的情况下, 或者在引用链的某一级上引用了一个第三方的对象, 而这个对象没有实现clone方法, 那么在它之后的所有引用的对象都是被共享的

person

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class Person implements Cloneable{

private Integer id;

private Student student;

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public Student getStudent() {
return student;
}

public void setStudent(Student student) {
this.student = student;
}

public Person(Student student) {
this.student = student;
}

@Override
protected Object clone() throws CloneNotSupportedException {

Person person = (Person) super.clone();

person.student = (Student) student.clone();

return person;
}
}

student

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
    public class Student implements  Cloneable{

private Man man;

private Giri giri;

public Man getMan() {
return man;
}

public void setMan(Man man) {
this.man = man;
}

public Giri getGiri() {
return giri;
}

public void setGiri(Giri giri) {
this.giri = giri;
}

private String name;

private Integer age;

public Student(String name,Integer age){
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

public Student(Man man, Giri giri) {
this.man = man;
this.giri = giri;
}

@Override
public String toString() {
return super.toString();
}

@Override
protected Object clone() throws CloneNotSupportedException {

Student student = (Student) super.clone();

student.man = (Man) man.clone();

student.giri = (Giri) giri.clone();

return student;
}
}

giri

1
2
3
4
5
6
7
8
public class Giri implements Cloneable{

@Override
protected Object clone() throws CloneNotSupportedException {

return super.clone();
}
}

man

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Man implements Cloneable{

private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

main

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) throws CloneNotSupportedException {

Person person = new Person(new Student(new Man(),new Giri()));

Person clone = (Person) person.clone();

System.out.println(person == clone);
System.out.println(person.getStudent() == clone.getStudent());
System.out.println(person.getStudent().getMan() == clone.getStudent().getMan());
System.out.println(person.getStudent().getGiri() == clone.getStudent().getGiri());

运行结果

1
2
3
4
false
false
false
false

简单图示剖析
clone()

多态

宏观意义上理解的多态

有继承

有重写

有父类引用指向子类对象

多态的底层原理

1
Person person = Math.random() > 0.5 ? new Student() : new Teacher();

等于号左侧是确定的,永远不能变,而右侧则不一样了,在代码未执行,其实我们也无法知道

等于号左边 叫做 【静态类型】,或者叫【编译类型】或者叫【申明类型】,或者叫【外观类型】

等于号右侧的我们叫【动态类型】,也叫【运行时类型】或者叫【实际类型】

对于静态类型,jvm在编译的时候就能确定具体调用哪个版本的方法,字节码指令执行时直接调用即可,而动态类型必须等待运行时才能确定类型,与此同时才能同步开展选择方法版本的工作,这个运行时才选择方法调用版本的行为称之为【虚方法分派】

常量池

常量池是我们的资源仓库,里边保存了大量的符号引用(就是的你给类、方法、变量的名字),这些符号引用有一部分会在类加载阶段或者第一次使用的时候就被转化为【直接引用】,这种转化叫做【静态解析】,另一部分会在运行期间转化为【直接引用】,这一部分称之为【动态链接】

假如说你和你的女朋友或者男朋友是异地恋 今天放假 你高兴的要去找你对象,那么你对象就是符号引用 你就是运行时 这个时候你在北京 它在大连 这个时候就可以将你找对象转换为你去大连 这个过程就是【静态解析】

假如说你的另一半是已经注定好的了,如果你不去寻找,那么最后可能就是注定的那个人,但是在成长的路上可能会遇见你喜欢的人,可能在初中遇见一个,高中遇见一个,大学遇见一个,社会上遇见一个,所以就是不到最后你都不知道那个人会是谁,不知道具体的位置,这个过程就是【动态链接】

你知道的越多 你不知道的越多 嘿 我是小博 带你一起看我目之所及的世界

-------------本文结束 感谢您的阅读-------------

本文标题:javaSE 面向对象 复习

文章作者:小博

发布时间:2022年01月27日 - 09:55

最后更新:2022年01月27日 - 09:57

原始链接:https://codexiaobo.github.io/posts/399676132/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。