JavaSE第2篇面向对象

1.类和对象的基本概念

面向对象编程语言(OOP)是区分高级编程语言和中级的一项重要指标。与面向过程语言不同的是,面向对象语言所考虑的是如何对现实中的事物抽象成一个类来进行处理。其中类是对一群具有相同特征的事物的集合,而对象是指具有这些特性的个体,类强调的是整体,对象强调的是个体

类中最重要的是属性(也叫字段field)与方法,属性是对类的的描述,方法是对类属性的操作。按照闭包的程序设计原则,类中的属性应当私有,需要访问的时候对外暴露共有的方法。对外暴露的方法在Java中一般使用get和set方法。定义一个对象通常是使用类来new

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
public class Main {
public static void main(String[] args) {
Person ming = new Person();
ming.setName("Xiao Ming"); // 设置name
ming.setAge(12); // 设置age
System.out.println(ming.getName() + ", " + ming.getAge());
}
}

class Person {
private String name;
private int age;

public String getName() {
return this.name;
}

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

public int getAge() {
return this.age;
}

public void setAge(int age) {
if (age < 0 || age > 100) {
throw new IllegalArgumentException("invalid age value");
}
this.age = age;
}
}

构造方法和toString方法

构造方法是特殊的一类方法,构造方法是方法名与类名相同的的方法。分为无参构造和有参构造。构造方法主要用于对类的初始化操作。当new一个对象时,会自动执行其构造方法,如果传递了参数会执行带参数的构造。

toString方法用来返回以一个字符串表示的对象值, @Override注解表示对方法的重写,通常情况下会对类的toString方法进行重写,以满足业务要求

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
public class Main {
public static void main(String[] args) {
Person ming = new Person("小明", 22);
System.out.println(ming);
}
}
class Person {
private String name;
private int age;
public Person() {
System.out.println("Person无参构造");
}

public Person(String name, int age) {
this(); //没有这一行不会执行无参构造
this.name = name;
this.age = age;
System.out.println("Person带参构造,name=" + name + ",age=" + age);
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

this和super的区别

this表示的是当前对象,从本质上讲,this是一个指向本对象的指针。super表示对父类的引用。如果带有参数表示的是引用的是构造函数。new一个对象的时候会首先默认调用父类的构造方法,然后再调用自身的构造方法。

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
public class ClassDemo {
public static void main(String[] args) {
Sun davie = new Sun();
davie.show();

Sun jack = new Sun(45);
jack.show();
}
}
class Father {
int age = 50;
public Father() {
System.out.println("Father无参构造");
}

public Father(int age) {
this.age = age;
System.out.println("Father有参构造,age=" + age);
}
}

class Sun extends Father {
int age = 20;

public Sun() {
System.out.println("Sun无参构造");
}

public Sun(int age) {
super(age); //不写会调用无参构造
this.age = age;
System.out.println("Sun有参构造,age=" + age);
}

void show() {
int age = 30;
System.out.println(age);
System.out.println(this.age);
System.out.println(super.age);
}
}

静态变量和静态方法

Java中其他的字段和方法在实例化的时候都会创建一个独立的内存空间,而静态变量和方法拥有共享的静态变量区和静态方法区。所有的静态内容会在类加载的时候分配内存空间而不是对象创建的时候分配。静态资源可以直接通过类名来访问。

类的初始化次序:

(静态变量、静态初始化块)–>(变量、初始化块)–> 构造器;如果有父类,则顺序是:父类static方法 –> 子类static方法 –> 父类构造方法- -> 子类构造方法

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
public class Main {
public static void main(String[] args) {
Person person = new Person();
System.out.println(Person.info);
}
}
class Person {
String name = test(); //这里我们用test方法的返回值作为变量的初始值,便于观察
int age;
String sex;

{
System.out.println("我是普通代码块");
}

Person() {
System.out.println("我是构造方法");
}

String test() {
System.out.println("我是成员变量初始化");
return "小明";
}

static String info = init(); //这里我们用init静态方法的返回值作为变量的初始值,便于观察

static {
System.out.println("我是静态代码块");
}

static String init(){
System.out.println("我是静态变量初始化");
return "test";
}
}

访问控制

Java中具有四种访问控制权限,四种访问控制权限可以作用与变量上,也可以作用与类上。需要额外注意的是一个类文件中只能有一个public类

当前类同一个包下的类不同包下的子类不同包下的类
public
protected
默认
private

2.继承、多态、封装

继承、多态、封装是面向对象的最基本的三项特征。前面的例子中的闭包原则就是封装的重要特性,继承的例子也通过了super和this关键字进行了演示,需要注意的是Java语言是单继承的机制,即一个子类只能有一个父类。类定义的时候加final关键字表示该类不可以被继承。java中的所有类都继承自一个顶层类Object,Object类中提供了toSring,equals等方法。

封装示例:单例设计模式

1
2
3
4
5
6
7
8
9
10
11
public class Person {
private String name;
private int age;
private String sex;

private Person(){} //不允许外部使用new关键字创建对象

public static Person getInstance() { //而是需要使用我们的独特方法来生成对象并返回
return new Person();
}
}

方法重写与重载

方法重写是子类与父类之间,一般用于扩展父类所拥有的方法。重载是在同一个类当中的,重载是指方法名字相同,而参数不同。返回类型可以相同也可以不同。是对方法参数和返回值的增强。方法的重写体现了类的多态性,另外父类可以初始化子类对象同样体现了多态性

区别点重载方法重写方法
参数列表必须修改一定不能修改
返回类型可以修改一定不能修改
异常可以修改可以减少或删除,一定不能抛出新的或者更广的异常
访问可以修改一定不能做更严格的限制(可以降低限制)

父类初始化子类对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Main {
public static void main(String[] args) {
Person p = new Student();
p.run();
}
}

class Person {
public void run() {
System.out.println("Person.run");
}
}

class Student extends Person {
@Override
public void run() {
System.out.println("Student.run");
}
}

抽象类

抽象类是对类的进一步抽象。通常来讲,一个类中定义了方法,但没有具体的执行代码,这个方法就是是抽象方法,而相对应的类称为抽象类。使用abstract关键字来声明。抽象类通常用于子类和父类之间。可以通过抽象类去引用具体的子类实例。而程序设计中,有面向抽象编程

  • 上层代码只定义规范(例如:abstract class 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
37
38
39
40
41
42
43
44
45
46
47
48
49
public class Main {
public static void main(String[] args) {
Person student = new Student("张三",18,"男") {
@Override
public void exam() {
super.exam();
}
};
student.exam();
Person worker = new Worker("李四",28,"男");
worker.exam();
}
}
abstract class Person { //通过添加abstract关键字,表示这个类是一个抽象类
protected String name; //大体内容其实普通类差不多
protected int age;
protected String sex;
protected String profession;

protected Person(String name, int age, String sex, String profession) {
this.name = name;
this.age = age;
this.sex = sex;
this.profession = profession;
}
public abstract void exam(); //抽象类中可以具有抽象方法,也就是说这个方法只有定义,没有方法体
}

class Worker extends Person{
public Worker(String name, int age, String sex) {
super(name, age, sex, "工人");
}

@Override
public void exam() { //子类必须要实现抽象类所有的抽象方法,这是强制要求的,否则会无法通过编译
System.out.println("打工人,打工混");
}
}

abstract class Student extends Person { //如果抽象类的子类也是抽象类,那么可以不用实现父类中的抽象方法
public Student(String name, int age, String sex) {
super(name, age, sex, "学生");
}

@Override //抽象类中并不是只能有抽象方法,抽象类中也可以有正常方法的实现
public void exam() {
System.out.println("DDL, DDL, DDL");
}
}

接口

接口是对抽象类的进一步抽象。如果一个抽象类中没有字段,所有方法都是抽象方法,那么就可以把他定义为接口。接口使用关键字interface。实现接口需要使用implements关键字。接口是可以进行多继承的。接口也是现在程序设计中最常用的方式。从java8开始,接口可以使用default关键字实现默认方法。接口不允许定义成员变量和成员方法,但可以定义静态变量和静态方法

abstract classinterface
继承只能extends一个class可以implements多个interface
字段可以定义实例字段不能定义实例字段
抽象方法可以定义抽象方法可以定义抽象方法
非抽象方法可以定义非抽象方法可以定义default方法

接口实现类深拷贝案例

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
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Student student = new Student("小明", 18, "男");
Student clone = (Student) student.clone(); //调用clone方法,得到一个克隆的对象
System.out.println(student);
System.out.println(clone);
System.out.println(student == clone);
student.stu();

Study.test();
System.out.println(Study.pi);
}
}
abstract class Person { //通过添加abstract关键字,表示这个类是一个抽象类
protected String name; //大体内容其实普通类差不多
protected int age;
protected String sex;
protected String profession;

protected Person(String name, int age, String sex, String profession) {
this.name = name;
this.age = age;
this.sex = sex;
this.profession = profession;
}
}

class Student extends Person implements Study, Cloneable { //首先实现Cloneable接口,表示这个类具有克隆的功能
public Student(String name, int age, String sex) {
super(name, age, sex, "学生");
}

@Override
public Object clone() throws CloneNotSupportedException { //提升clone方法的访问权限
return super.clone(); //因为底层是C++实现,我们直接调用父类的实现就可以了
}

@Override
public void study() {
System.out.println("我会学习!");
}
}

interface Study {
public static final float pi = 3.14f; //接口中定义的静态变量只能是public static final的

static void test(){ //接口中定义的静态方法也只能是public的
System.out.println("我是静态方法");
}

void study();

default void stu() { //使用default关键字为接口中的方法添加默认实现
System.out.println("我是默认实现");
}
}

枚举类

枚举类通常是用来记录状态信息,例如星期,颜色这类的信息。枚举类使用的关键字是enum。枚举常量本来担忧类型的信息,编译器是可以检查类型的错误。不同的枚举不能相互比较或赋值

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
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Student student = new Student("小明", 18, "男");
student.setStatus(Status.RUNNING);
System.out.println(student.getStatus().getName());
}
}
abstract class Person { //通过添加abstract关键字,表示这个类是一个抽象类
protected String name; //大体内容其实普通类差不多
protected int age;
protected String sex;
protected String profession;

protected Person(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
}

enum Status {
RUNNING("跑步"), STUDY("学习"), SLEEP("睡觉"); //无参构造方法被覆盖,创建枚举需要添加参数(本质就是调用的构造方法)

private final String name; //枚举的成员变量
Status(String name) { //覆盖原有构造方法(默认private,只能内部使用!)
this.name = name;
}

public String getName() { //获取封装的成员变量
return name;
}
}

class Student extends Person implements Study {

private Status status; //类型变成刚刚定义的枚举类

protected Student(String name, int age, String sex) {
super(name, age, sex);
}

public Status getStatus() {
return status;
}

public void setStatus(Status status) {
this.status = status;
}

@Override
public void study() {
System.out.println("我会学习!");
}
}

interface Study {
void study();
}

JavaSE第2篇面向对象
https://www.eldpepar.com/coding/51735/
作者
EldPepar
发布于
2022年10月16日
许可协议