Java面向对象细碎知识

Java面向对象细碎知识

基础知识

引用变量:

​ 引用类型变量(Reference Type Variable)是一种编程概念,主要存在于面向对象语言如Java、C#等中。引用类型变量并不直接存储对象的值,而是存储对该对象的引用或者说地址。这意味着它是一个指针或者句柄,指向内存中某个位置——存储了实际的对象数据

在Java中,除了基本数据类型(如int、char、double等)以外,其他的类型如类(class)、接口(interface)、数组(array)都属于引用类型。当声明一个引用类型变量时,实际上创建了一个引用

对象的内存分配

对象的内存分配

​ 声明一个引用类型变量时,系统只为该变量分配了引用空间,并未创建具体对象;当用new为对象分配空间后,将对象的引用赋值给引用变量

类的声明:

1
[public] [abstract|final] class ClassName [extends SuperClassName] [implements InterfaceNameList] { … }

成员方法:

  • 方法是传值的,方法调用不会改变参数的值。即调用时参数传递给方法,就是赋值的过程

  • 当对象作为参数时,参数的值是该对象的引用,这时对象的内容可以在方法中改变,但是对象的引用不会改变(这不意味着没有局部变量的概念),例如:

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
public class Person {
public String name;

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

public class Main {
public static void main(String[] args) {
// 创建Person对象,作为局部变量
Person person = new Person();
person.name = "Alice";

// 将对象作为参数传递给方法,并在方法内部修改对象内容
changeName(person);

// 方法外部仍然可以看到对象内容的变化
System.out.println(person.name); // 输出 "Bob"
}

public static void changeName(Person p) {
// 参数p是对person对象的一个引用
p.name = "Bob"; // 此处修改的是person对象的实际内容
}
}
  • 可变参数列表Varargs(类似 Python 的 args):定义格式:类型 … 参数名,可以使方法具有数目不定的多个参数,只能作为方法参数列表中的最后一个

类的构造方法:

所有的类都有构造方法,用来进行该类对象的初始化

1
[public|protected|private] ClassName ([<argument_list>]) { … }
  • 方法名必须与类名相同

  • 不能带返回值

  • 不是类的方法,不称为类的成员

  • 不能直接调用

  • 如果在类定义中无构造方法,Java在编译时自动加入默认构造方法

  • 一旦在类中有一个自己声明的构造方法,则默认的构造方法将不被加到类的定义中

this关键字:

​ 关键字this 用来指向当前对象本身

​ 当类中有两个同名变量,一个属于类(类的成员变量),而另一个属于某个特定的方法(方法中的局部变量),使用this区分成员变量和局部变量

1
2
3
4
5
6
7
8
class MyDate {
private int day, month, year;
public MyDate(int day, int month, int year){
this.day = day;
this.month = month;
this.year = year;
}
}

super关键字:

​ super 指向该关键字所在类的父类。可以使用super访问父类被子类隐藏的变量或覆盖的方法

​ 缺省是不带参数的构造方法。如果需要调用特殊的父类构造方法,则需在子类构造方法中第一行通过super( … )调用

​ 在方法中调用构造方法用this(),调用父类的构造方法用super(),而且该语句要出现在子类构造方法的第一行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Empolyee {
private String name ;
private int salary;
public String getDetails( ){
return "Name: "+name+"\nSalary:"+salary;
}
}

public class Manager extends Empolyee {
private String department ;
public String getDetails( ){
return super.getDetailes( )+"\nDepartment: "+
department;
}
}

访问控制:

  • 成员变量和方法有4种访问级别:public, protected, default(package), private(带private 修饰符的属性、方法是不能被继承的,其他的都可以继承)

  • 类有两种访问级别:public 或default

修饰符的作用范围:

Modifier Same Class Same Package Subclass Universe
public Yes Yes Yes Yes
protected Yes Yes Yes No
default Yes Yes No No
private Yes No No No
本人/克隆体 自己家 独立子女 所有人

注意:子类不能直接访问不在同一个包中的父类的protected变量和方法,只能通过自身(或子类)访问

protected访问修饰符允许以下情况:

  • 同一包内的其他类可以访问protected变量和方法
  • 不同包中的类,但仅限于子类,可以访问protected变量和方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package Greek; 
public class Alpha {
protected int iamprotected;
protected void protectedMethod() {
System.out.println("protectedMethod");
}
}
package Latin;
import Greek.*;
class Delta extends Alpha {
void accessMethod(Alpha a, Delta d) {
a.iamprotected = 10; // x
d.iamprotected = 10; // y
a.protectedMethod(); // x
d.protectedMethod(); // y
}
}

继承与初始化

  • Java是单继承的,即只能从一个类继承,extends后类名只能有一个
  • 单继承的优点:
    • 代码更可靠
    • 可以用接口弥补
    • 用一个类实现多个接口,达到多继承效果

对象的初始化过程:

在对对象初始化时,顺序如下:

  • 静态成员
    • 静态初始化块(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
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
class Meal {
Meal() {
System.out.println("Meal()");
}
static {
System.out.println("Meal Static statement");
}
{
System.out.println("Meal Field");
}
}

class Bread {
Bread() {
System.out.println("Bread()");
}
static {
System.out.println("Bread Static statement");
}
}

class Cheese {
Cheese() {
System.out.println("Cheese()");
}
}

class Lettuce {
Lettuce() {
System.out.println("Lettuce()");
}
}

class Lunch extends Meal {
static {
System.out.println("Lunch Static statement");
}
Lunch() {
System.out.println("Lunch()");
}
{
System.out.println("Lunch Field");
}
}

class PortableLunch extends Lunch {
static {
System.out.println("Portable Lunch Static statement");
}
PortableLunch() {
System.out.println("PortableLunch()");
}
}

public class Sandwich extends PortableLunch {
private Bread b = new Bread();
private Cheese c = new Cheese();
private Lettuce l = new Lettuce();

public Sandwich() {
System.out.println("Sandwich()");
}

static {
System.out.println("Static statement");
new Sandwich();
}

public static void main(String[] args) {
Bread h = new Bread();
new Sandwich();
}
}

>>> \\输出:
Meal Static statement
Lunch Static statement
Portable Lunch Static statement
Static statement
Meal Field
Meal()
Lunch Field
Lunch()
PortableLunch()
Bread Static statement
Bread()
Cheese()
Lettuce()
Sandwich()
Bread()
Meal Field
Meal()
Lunch Field
Lunch()
PortableLunch()
Bread()
Cheese()
Lettuce()
Sandwich()

下溯造型和上溯造型:

  1. 上溯造型(Upcasting): 上溯造型是指将子类对象转换为父类类型的过程。在Java中,这个过程是隐式进行的。上溯造型之所以称为“上溯”,是因为在类的继承树结构中,是从子类“上溯”到父类。这样做是安全的,因为子类对象本来就是父类的实例。

    1
    2
    3
    4
    5
    class Animal {}
    class Dog extends Animal {}

    Dog myDog = new Dog();
    Animal myAnimal = myDog; // 这里发生了上溯造型

    Java中允许构造如下类型的数组:

    1
    2
    3
    4
    5
    Employee [ ]  staff = new Employee[1024];
    staff[0] = new Manager();
    staff[1] = new Worker();
    staff[2] = new Employee();

  2. 下溯造型(Downcasting): 下溯造型则是将父类类型的对象显式地转换为它的子类类型。这个过程是不安全的,因为在转换前并不能确定父类对象是否真的是目标子类的实例。下溯造型有可能失败,尤其是在运行时,如果没有正确的类型检查,将会导致ClassCastException异常。使用关键字(类型)进行强制类型转换

    1
    2
    Animal animal = new Dog();
    Dog dog = (Dog) animal; // 这里发生了下溯造型

    在进行下溯造型之前,通常会使用instanceof关键字来检查对象是否可以安全地转换为指定的子类类型,防止运行时错误。

    1
    2
    3
    if (animal instanceof Dog) {
    Dog dog = (Dog) animal; // 确保animal是Dog类型后再转换
    }

内部类

  • 内部类是在一个类的声明里声明的类。
1
2
3
4
5
6
7
class A {
...
class B {
...
}
...
}
  • 可作为类的一个成员使用,一般只在外包类中调用
    1. 在类中定义一个类(私有内部类,静态内部类)
    2. 在方法中定义一个类(局部内部类,匿名内部类),意义:完善多重继承、实现事件驱动系统、闭包

编译器对类中内部类做的手脚:

  1. 在内部类中偷偷摸摸地创建了包可见构造器,从而使外部类获得了创建权限
  2. 在外部类中偷偷摸摸地创建了访问私有变量的静态方法,从而使内部类获得了访问权限。这样,类中定义的内部类无论私有、公有、静态都可以被包围它的外部类所访问
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Outer {
private int index = 100;
void print() {
Inner i = new Inner();
i.print();
}
class Inner {
void print() {
System.out.println(index);
}
}
}
class Test {
public static void main(String[] args) {
Outer o = new Outer();
o.print();
}
}

>>>