【笔记】Java的面向对象

前言

Java的面向对象(oop)学习笔记

  • 一个.java文件可以定义多个类,但只能有一个由public修饰的类,文件名需要与public修饰的类的类名保持一致
  • 类名首字母大写
类名.java
1
2
3
4
5
6
7
8
9
10
11
public class 类名 {
...
}

class 类名1 {
...
}

class 类名2 {
...
}

对象

  • 通过类创建对象
  • 对象名首字母小写
1
类名 对象名 = new 类名();

属性

  • 类中定义属性
  • 属性名首字母小写
1
2
3
class 类名 {
数据类型 属性名;
}

方法

  • 类中定义方法
  • 方法名首字母小写
1
2
3
4
5
6
7
8
class 类名 {
返回值类型 方法名(参数列表) {

...

return 返回值;
}
}

方法的参数列表

1
2
3
4
5
class 类名 {
void 方法名(数据类型 参数名) {
...
}
}

可变长参数

  • 可变长参数得到的是一个数组
  • 可变长参数必须在形参列表最后定义
1
2
3
4
5
class 类名 {
void 方法名(数据类型 参数名1, 数据类型... 参数名2) {
数据类型[] 变量名 = 参数名2;
}
}

方法的重载

  • 同一个类中定义多个同名方法,方法名相同,参数列表不同
1
2
3
4
5
6
7
8
class 类名 {
void 方法名(数据类型 参数名1) {
...
}
void 方法名(数据类型 参数名1, 数据类型 参数名2) {
...
}
}

方法的返回值

1
2
3
4
5
6
7
8
class 类名 {
数据类型 方法名() {

...

return 返回值;
}
}

构造方法

  • 构造方法名与类名同名
  • 构造方法没有返回值类型,也无需使用void关键字
  • 没有定义构造方法时,类中默认包含一个无参构造方法
  • 通过new关键字创建对象时,构造方法会被执行
1
2
3
4
5
class 类名 {
类名(参数列表) {
...
}
}

this

  • 通过this访问当前实例
  • this只能在方法中使用
  • 方法中访问实例属性和实例方法时可以省略this.

访问实例属性

1
2
3
4
5
6
7
8
class 类名 {
数据类型 属性名;

void 方法名() {
System.out.println(this.属性名);
System.out.println(属性名);
}
}

调用实例方法

1
2
3
4
5
6
7
8
9
10
class 类名 {
void 方法名() {
...
}

void 方法名2() {
this.方法名();
方法名();
}
}

调用构造方法

  • 通过this()调用当前类的其他构造方法
  • this()只能用在构造方法中
  • this()必须位于构造方法的第一行
1
2
3
4
5
6
7
8
9
class 类名() {
类名() {
...
}

类名(int a) {
this();
}
}

final

修饰变量

  • final修饰的变量,值不能被修改
1
final 数据类型 变量名 = 值;

修饰类

  • final修饰的类不能被继承
1
final class 类名 {}

修饰属性

  • final修饰的属性,值不能被修改
1
final 数据类型 属性名 = 值;

修饰方法

  • final修饰的方法不能被重写
1
final void 方法名() {}

static

  • 通过类名直接访问

静态属性

1
2
3
4
5
6
7
8
9
class 类名 {
static 数据类型 属性名;
}

class Main {
public static void main(String[] args) {
System.out.println(类名.属性名);
}
}

静态方法

  • 静态方法中只能访问静态属性和静态方法
1
2
3
4
5
6
7
8
9
10
11
class 类名 {
static void 方法名() {
...
}
}

class Main {
public static void main(String[] args) {
类名.方法名();
}
}

静态代码块

  • 静态代码块会在类加载时执行且仅执行一次
1
2
3
4
5
class 类名 {
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
class Cls {
Cls() {
System.out.println("构造方法");
}

static int a = initA();
static int initA() {
System.out.println("静态属性初始化");
return 1;
}

int b = initB();
int initB() {
System.out.println("实例属性初始化");
return 1;
}

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

class Main {
public static void main(String[] args) {
Cls cls = new Cls();
}
}
1
2
3
4
5
静态属性初始化
静态代码块
成员属性初始化
普通代码块
构造方法

继承

  • 通过extends继承父类
  • 子类只能继承一个父类
  • 子类继承父类后,子类实例可以访问父类属性和方法
1
2
3
4
5
6
7
class Father {
...
}

class Son extends Father {
...
}

方法的重写

  • 子类重写父类同名方法
  • @Override注解用于标注这是重写的方法
1
2
3
4
5
6
7
8
9
10
11
12
class Father {
public void 父类方法名() {
...
}
}

class Son extends Father {
@Override
public void 父类方法名() {
...
}
}

super

  • 通过super访问父类实例
  • super只能在方法中使用

访问父类实例属性

1
2
3
4
5
6
7
8
9
class Father {
数据类型 属性名;
}

class Son extends Father {
public void 方法名() {
System.out.println(super.属性名);
}
}

访问父类实例方法

1
2
3
4
5
6
7
8
9
10
11
class Father {
public void 父类方法名() {
...
}
}

class Son extends Father {
public void 子类方法名() {
super.父类方法名();
}
}

访问父类构造方法

  • 通过super()调用父类构造方法
  • super()只能用在构造方法中
  • super()必须位于子类构造方法的第一行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Father {
public Father() {
...
}
}

class Son extends Father {
public Son() {
super();

...

}
}

抽象类和抽象方法

定义抽象类

  • 通过abstract关键字修饰的类是抽象类
1
2
3
abstract class 抽象类名 {
...
}

抽象类中定义抽象方法

  • 通过abstract关键字修饰的方法是抽象方法,抽象方法不需要定义方法体
  • 抽象类中定义的方法可以是抽象的也可以不是抽象的
1
2
3
4
5
6
7
abstract class 抽象类名 {

abstract void 抽象方法名();

void 普通方法名() {}

}

子类继承抽象类

  • 子类通过extends继承抽象类
    • 子类只能继承一个父类
    • 子类继承抽象类时必须重写抽象类中的所有抽象方法
1
2
3
4
5
6
class 子类 extends 抽象类名 {
@Override
void 抽象方法名() {
...
}
}
  • 如果不重写接口定义的所有抽象方法,则子类一定也是抽象类
1
2
3
4
abstract class 子类 extends 抽象类名 {
@Override
abstract void 抽象方法名();
}

接口

定义接口

  • 通过interface关键字定义接口
1
2
3
interface 接口名 {
...
}

接口中定义抽象方法

  • 接口中定义的普通方法一定都是抽象的
1
2
3
interface 接口名 {
public abstract void 抽象方法名();
}
  • 接口中定义抽象方法时可以省略publicabstract关键字
1
2
3
interface 接口名 {
void 抽象方法名();
}

接口中抽象方法的默认实现

1
2
3
4
5
interface 接口名 {
default void 抽象方法名() {
...
}
}

接口中定义常量属性

  • 接口中的属性一定都是常量
1
2
3
interface 接口名 {
public static final 数据类型 属性名 = 值;
}
  • 接口中定义常量属性时可以省略publicstaticfinal关键字
1
2
3
interface 接口名 {
数据类型 属性名 = 值;
}

接口中定义静态方法

1
2
3
4
5
interface 接口名 {
public static void 静态方法名() {
...
}
}
  • 接口中定义静态方法时可以省略public关键字
1
2
3
4
5
interface 接口名 {
static void 静态方法名() {
...
}
}

子类实现接口

  • 子类通过implements实现接口
    • 子类可以实现多个接口,多个接口名之间用,分隔
    • 子类实现接口时必须重写所有接口定义的所有抽象方法
1
2
3
4
5
6
7
8
9
10
11
12
interface 接口名1 {
void 抽象方法名();
}

interface 接口名2 {}

class 类名 implements 接口名1, 接口名2 {
@Override
public void 抽象方法名() {
...
}
}
  • 如果不重写接口定义的所有抽象方法,则子类一定是抽象类
1
2
3
4
5
6
7
8
9
10
interface 接口名1 {
void 抽象方法名();
}

interface 接口名2 {}

abstract class 类名 implements 接口名1, 接口名2 {
@Override
public abstract void 抽象方法名();
}

多态

父类引用指向子类对象

1
父类名 对象名 = new 子类名();

父类引用转换为子类对象

1
2
3
4
父类名 对象名 = new 子类名();
if (对象名 instanceof 子类名) {
子类名 子对象名 = (子类名) 对象名;
}

权限修饰符

|关键字|权限|当前类|当前类及子类|当前包|当前项目|
|—|—|—|—|—|
|public|公共权限|✓|✓|✓|✓|
|protected|保护权限|✓|✓|✓|×|
|无修饰符||✓|✓|×|×|
|private|私有权限|✓|×|×|×|

  • 类的权限修饰符只能是public和无修饰符
1
2
3
4
5
6
7
8
9
10
11
class 类名 {

public void 方法名() {}

protected void 方法名() {}

void 方法名() {}

private void 方法名() {}

}

包管理

package

  • 通过package定义包名
1
2
3
4
5
package 包名;

class 类名 {
...
}

import

引入指定包中的指定类

1
import 包名.类名;

引入指定包中的所有类

1
import 包名.*;

直接引入静态类中的属性或方法

1
2
import static 包名.类名.属性名;
import static 包名.类名.方法名;

直接通过类的全局限定名访问类

1
包名.类名 对象名 = new 包名.类名();

成员内部类

定义内部类

1
2
3
4
5
class Outer {
class Inner {
...
}
}

创建对象

在外部类中创建内部类对象

1
2
3
4
5
6
7
8
9
10
11
12
class Outer {
class Inner {
int field;
void method() {}
}

void fn() {
Inner inner = new Inner();
System.out.println(inner.field);
inner.method();
}
}

在其他类中创建内部类对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Outer {
class Inner {
int field;
void method() {}
}
}

public class Cls {
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
System.out.println(inner.field);
inner.method();
}
}

内部类访问外部类的属性和方法

  • 内部类没有同名属性或方法,可以直接调用
1
2
3
4
5
6
7
8
9
10
11
12
class Outer {

int field;
void method() {}

class Inner {
void fn() {
System.out.println(field);
method();
}
}
}
  • 内部类有同名属性或方法,需要通过类名访问
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Outer {

int field;
void method() {}

class Inner {

int field;
void method() {}

void fn() {
System.out.println(Outer.field);
Outer.method();
System.out.println(field);
method();
}
}
}
  • 外部类属性名、内部类属性名、方法内变量名重名时,需要显式指定访问的属性或变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Outer {

int field;

class Inner {

int field;

void fn(int field) {
System.out.println(Outer.this.field);
System.out.println(this.field);
System.out.println(field);
}
}
}

静态内部类

定义内部类

1
2
3
4
5
class Outer {
static class Inner {
...
}
}

定义内部接口

  • 内部接口一定都是静态的
1
2
3
4
5
class Outer {
static interface Inner {
...
}
}
  • 定义内部接口时可以省略static关键字
1
2
3
4
5
class Outer {
interface Inner {
...
}
}

创建对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Outer {
static class Inner {
int field;
void method() {}
}
}

public class Cls {
public static void main(String[] args) {
Outer.Inner inner = new Outer.Inner();
System.out.println(inner.field);
inner.method();
}
}

局部内部类

  • 在代码块中定义的内部类只能在代码块中创建对象

定义内部类

1
2
3
4
5
6
7
class Outer {
void fn() {
class Inner {
...
}
}
}

创建对象

1
2
3
4
5
6
7
8
9
10
11
12
class Outer {
void fn() {
class Inner {
int field;
void method() {}
}

Inner inner = new Inner();
System.out.println(inner.field);
inner.method();
}
}

匿名内部类

通过匿名内部类为抽象类或接口创建对象

  • 创建对象时,通过匿名内部类,直接重写接口或抽象类的抽象方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
abstract class Cls {
void method();
}

public class Main {
public static void main(String[] args) {
Cls cls = new Cls() {
@Override
public void method() {
...
}
};
cls.method();
}
}

Lambda表达式

  • 有且只有一个抽象方法的接口使用匿名内部类时,可以将匿名内部类简写为Lambda表达式
1
2
3
4
5
6
7
8
9
10
11
12
interface Cls {
int method(int arg);
}

public class Main {
public static void main(String[] args) {
Cls cls = (int arg) -> {
return 0;
};
cls.method();
}
}
  • 如果方法体只有一行,那么可以省略{}return关键字
1
2
3
4
5
6
7
8
9
10
interface Cls {
int method(int arg);
}

public class Main {
public static void main(String[] args) {
Cls cls = (int arg) -> 0;
cls.method();
}
}
  • 如果形参只有一个,那么可以省略()
1
2
3
4
5
6
7
8
9
10
11
12
interface Cls {
int method(int arg);
}

public class Main {
public static void main(String[] args) {
Cls cls = arg -> {
return 0;
};
cls.method();
}
}

方法引用

  • 有且只有一个抽象方法的接口使用匿名内部类时,如果接口的这个方法的形参列表与返回值类型与其他方法的形参列表与返回值类型都相同,可以使用::作为方法引用
实例方法引用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Cls {
int methodFromCls(int num) {
return num;
}
}

interface Inf {
int methodFromInf(int num);
}

public class Main {
public static void main(String[] args) {
Cls cls = new Cls();
Inf inf = cls::methodFromCls;
inf.methodFromInf(0);
}
}
静态方法引用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Cls {
static int methodFromCls(int num) {
return num;
}
}

interface Inf {
int methodFromInf(int num);
}

public class Main {
public static void main(String[] args) {
Inf inf = Cls::methodFromCls;
inf.methodFromInf(0);
}
}
  • 有且只有一个抽象方法的接口使用匿名内部类时,如果接口的这个方法的形参为指定类型,返回值类型与指定类型的指定方法的返回值类型相同,也可以使用::作为方法引用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Cls {
int methodFromCls() {
return 0;
}
}

interface Inf {
int methodFromInf(Cls cls);
}

public class Main {
public static void main(String[] args) {
Inf inf = Cls::methodFromCls;
inf.methodFromInf(new Cls());
}
}
构造方法引用
  • ::后通过new关键字引用构造方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Cls {
Cls(int num) {
...
}
}

interface Inf {
Cls methodFromInf(int num);
}

public class Main {
public static void main(String[] args) {
Inf inf = Cls::new;
inf.methodFromInf(0);
}
}

通过匿名内部类创建对象并作为方法形参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
abstract class Cls {
int method(int arg);
}

public class Main {

static void fn(Cls cls) {
System.out.println(cls);
}

public static void main(String[] args) {
fn(new Cls() {
@Override
public void method() {
...
}
});
}
}

完成

参考文献

哔哩哔哩——青空の霞光