类与对象

面向对象语言

  • 封装性
  • 继承性
  • 多态性
  • 抽象性

可见性

在Java中,方法的访问修饰符控制着方法的可见性,即它们可以被哪些其他类或实例访问。除了访问修饰符之外,还有一些非访问修饰符,它们提供了其他类型的控制或信息。以下是这些修饰符的说明:

访问修饰符

  1. public

    • 任何其他类都可以访问 public 方法,不论它们是否在同一个包中。
  2. private

    • private 方法只能被它们所属的类内部访问。它们不能被其他类或子类访问。
  3. protected

    • protected 方法可以被同一个包内的其他类访问,也可以被不同包中的子类访问。
  4. 默认(无修饰符)

    • 如果没有指定访问修饰符,那么方法具有默认的包访问级别,即只能被同一个包内的其他类访问。

非访问修饰符

  1. static

    • static 方法属于类本身,而不是类的任何对象。这意味着你可以在没有创建类的对象的情况下调用 static 方法。
    • 它们通常用于工具方法,如数学计算,或用于初始化操作,如加载资源。
  2. final

    • final 方法不能被子类覆盖。这可以用来防止修改方法的行为,确保它在继承体系中保持不变。
  3. abstract(抽象的):

    • abstract 方法没有实现,它们只声明了方法的签名。包含 abstract 方法的类必须是抽象类,不能被实例化。
    • 抽象类和方法用于定义接口或行为的蓝图,具体的实现由子类提供。
  4. synchronized

    • synchronized 方法在多线程环境中用于防止多个线程同时执行该方法,确保线程安全。
  5. native

    • native 方法是使用Java以外的语言(通常是C或C++)实现的。它们提供了一种从Java代码调用非Java代码的方式。

示例

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
public class MyClass {

// 公有方法,可以被任何其他类访问
public void publicMethod() {
// ...
}

// 私有方法,只能被当前类访问
private void privateMethod() {
// ...
}

// 受保护的方法,可以被同一个包内的其他类或子类访问
protected void protectedMethod() {
// ...
}

// 默认访问级别方法,只能被同一个包内的其他类访问
void defaultMethod() {
// ...
}

// 静态方法,属于类而不是对象,可以在没有创建对象的情况下调用
public static void staticMethod() {
// ...
}

// 最终方法,不能被子类覆盖
public final void finalMethod() {
// ...
}

// 抽象方法,没有实现,类必须是抽象类
public abstract void abstractMethod();

// 同步方法,确保线程安全
public synchronized void synchronizedMethod() {
// ...
}

// 本地方法,用非Java语言实现
public native void nativeMethod();

// 一个方法可以同时使用多个修饰符
public static final void anotherMethod() {
// ...
}
}

在设计类和方法时,合理使用这些修饰符对于控制访问级别、确保线程安全、提供类的行为约束等都是非常重要的。

1
2
3
4
5
6
7
8
9

### 类

#### 基本格式

```java
class 类名{
类体的内容
}

类的声明

1
2
3
4
5
6
7
class People{
...
}

class 植物{
...
}

类体

1
2
3
4
5
6
7
8
9
10
11
12
13
class Ladder{
float above;
float bottom;
float height;
float area;
float computer(){
area=(above+bottom)*height/2.0f;
return area;
}
void setHeight(float h){
height=h;
}
}

成员变量

成员变量可以是Java中任何一种数据类型

  • 逻辑型(boolean:默认值是 false
  • 整数型(byte, short, int, long):所有整数类型的默认值是 0
  • 浮点型(float, double)float 类型的默认值是 0.0fdouble 类型的默认值是 0.0d
  • 字符型(char):默认值是空字符 \u0000,(教材写作NULL)即数值为0的字符。

这些默认值适用于类的成员变量(也称为字段或属性),不适用于局部变量。局部变量不会自动初始化为默认值,必须在使用前显式初始化。

成员变量的有效范围

在整个类的所有的方法里都有效,其有效与它在类体中出现的位置无关

上述Ladder类等效于

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Ladder{
float above;
float bottom;

float area;
float computer(){
area=(above+bottom)*height/2.0f;
return area;
}
float height;
void setHeight(float h){
height=h;
}
}

方法

1
2
3
4
5
6
7
8
int speak() //无参
{
return 23;
}
int add(int x,int y,int z)//有参
{
return x+y+z;
}

注:当一个方法是void类型,该方法不需要返回值

方法体

方法体中声明的变量和方法的参数被称为局部变量

局部变量只在方法内有效

如果局部变量是在循环语句中,那么该局部变量的有效范围是在该循环语句内

区分局部变量和成员变量

如果局部变量和成员变量的名字相同,那么成员变量被隐藏,即该成员变量在这个方法内暂时失效(就近原则

1
2
3
4
5
6
7
class temple1 {
int x=10,y;
void f(){
int x=5;
y=x+x;//y=10
}
}

如果想在该方法中使用被隐藏的成员变量,必须使用关键字this指定this.成员变量

1
2
3
4
5
6
7
class temple2 {
int x=10,y;
void f(){
int x=5;
y=x+this.x;//y=15
}
}

注:局部变量没有默认值

构造方法

构造方法在面向对象编程中扮演着至关重要的角色,因为它们确保了对象在使用前能够被正确地初始化。

构造方法的名字必须与所在的类的名字完全相同,而且没有类型

一个类中可以有多个构造方法但必须保证他们的参数不同(个数,类型不同)

构造方法的主要作用是初始化对象的状态,即设置对象在开始时应具有的值和状态。这是确保对象在被使用之前已经准备好其必要的数据和资源的关键步骤。

  1. 名称:构造方法的名称必须与定义它的类完全相同。
  2. 返回类型:构造方法没有返回类型,即使是void也不行。
  3. 重载:一个类可以有多个构造方法,只要它们的参数列表不同(不同的参数个数或类型)。
  4. 初始化:构造方法的主要目的是初始化对象的状态,设置初始值和资源。
  5. 默认构造方法:如果类中没有定义任何构造方法,编译器会提供一个无参的默认构造方法,其方法体为空。

构造方法的作用:

  • 为新对象分配内存。
  • 初始化对象的属性。
  • 执行任何需要的设置或资源分配。

自定义构造方法的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Point {
int x, y;

// 无参构造方法,默认初始化x和y为1
Point() {
this.x = 1;
this.y = 1;
}

// 带有两个参数的构造方法,用于指定x和y的值
Point(int a, int b) {
this.x = a;
this.y = b;
}
}

在这个例子中,Point 类提供了两种不同的构造方法:

  1. 无参构造方法:当使用 new Point() 创建对象时,会调用此构造方法,并将 xy 初始化为默认值1。
  2. 参数化构造方法:当使用 new Point(2, 3) 创建对象时,会调用此构造方法,并根据提供的参数初始化 xy

构造方法的调用:

  • 构造方法在创建类的新实例时自动调用。
  • 可以通过 new 关键字后跟构造方法名和相应的参数来调用构造方法。

注:如果没有编写构造方法,系统会默认该类只有一个构造方法,该默认构造方法是无参的,且方法体中没有语句。例:

1
2
Ladder(){
}

构造方法重载

构造方法重载允许一个类拥有多个同名但参数不同的构造方法,这样可以根据需要选择使用不同的构造方法来创建对象。这增加了类的灵活性,使得类的实例化过程可以更加多样和方便。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Student {
String name;
int age;
double gpa;

// 无参构造方法
public Student() {
this("Unknown", 0, 0.0);
}

// 带有两个参数的构造方法
public Student(String name, int age) {
this(name, age, 0.0);
}

// 带有三个参数的构造方法
public Student(String name, int age, double gpa) {
this.name = name;
this.age = age;
this.gpa = gpa;
}
}

在这个例子中,Student 类有三个构造方法,它们都叫做 Student,但参数列表不同。无参构造方法使用默认参数值来调用带有三个参数的构造方法。带有两个参数的构造方法同样调用带有三个参数的构造方法,但是为 gpa 提供了默认值。

当创建 Student 类的对象时,可以根据需要选择调用不同的构造方法:

1
2
3
Student student1 = new Student(); // 使用无参构造方法
Student student2 = new Student("Alice", 20); // 使用两个参数的构造方法
Student student3 = new Student("Bob", 22, 3.5); // 使用三个参数的构造方法

构造方法重载使得 Student 类可以以不同的方式初始化,而不必修改类的设计。这在实际编程中非常有用,尤其是在需要处理多种不同情况的类设计中。


对象

对象是面向对象编程(OOP)中的一个基本概念,代表现实世界中的实体或概念。在Java中,对象是通过类的实例化创建的,并且每个对象都拥有其自己的状态和行为。

对象的声明与创建

  • 声明:在Java中,对象的声明涉及指定对象的类型和名称。

    1
    类名 对象名;

    例如:

    1
    Ladder ladder;
  • 创建:使用 new 关键字后跟类的构造方法来创建对象。new 运算符负责分配内存并调用构造方法以初始化对象。

    1
    类名 对象名 = new 类名(参数列表);

    例如:

    1
    2
    Point p1 = new Point(10, 20);
    Point p2 = new Point(23, 25);

对象的内存分配

  • 栈与堆:在Java中,对象的内存分配涉及两个主要区域:栈和堆。
    • 栈(Stack):用于存储基本数据类型和对象引用(指针)。栈由JVM自动管理,存取速度快,但大小固定,不够灵活。
    • 堆(Heap):用于存储对象的实际数据。堆的大小可以动态变化,JVM负责垃圾回收,自动清理不再使用的对象。

创建多个对象

  • 一个类可以通过 new 运算符创建多个对象,每个对象都拥有独立的内存空间。
    1
    2
    Xiyoujirenwu zhubajie = new XiyoujiRenwu();
    Xiyoujirenwu sunwukong = new XiyoujiRenwu();

使用对象

  • 访问变量:通过点(.)操作符,可以访问对象的成员变量。

    1
    对象.变量;
  • 调用方法:同样使用点操作符来调用对象的成员方法。

    1
    对象.方法();

对象的生命周期

  • 对象从 new 表达式被执行时开始创建,直到没有任何引用指向它时结束。Java的垃圾收集器会定期检查并释放未被引用的对象所占用的内存。

示例

以下是如何使用对象的完整示例:

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
class Xiyoujirenwu {
float height, weight;
String head, ear;

void speak(String s) {
head = "歪着头";
System.out.println(s);
}
}

public class test1 {
public static void main(String[] args) {
Xiyoujirenwu zhubajie = new Xiyoujirenwu(); // 创建对象
zhubajie.height = 1.8f;
zhubajie.weight = 1.62f;
zhubajie.head = "大头";
zhubajie.ear = "一双大耳朵";

System.out.println("猪八戒的头为:" + zhubajie.head);
System.out.println("猪八戒的身高为:" + zhubajie.height);
System.out.println("猪八戒的体重为:" + zhubajie.weight);

zhubajie.speak("俺老朱想娶媳妇");

System.out.println("猪八戒的头为:" + zhubajie.head);
}
}

在这个示例中,Xiyoujirenwu 类的对象 zhubajie 被创建并初始化,然后通过点操作符访问其变量和方法。

对象的引用和实体

分配给对象的变量被习惯地称作对象的实体

1、避免使用空对象

2、重要结论

一个类声明的两个对象如果具有相同的引用,两者就具有完全相同的变量(实体)

在JAVA中,对于同一个类的两个对象object1和object2,允许进行如下操作:

1
object1=object2;

这样object2将引用指向object1,即object1,object2共同指向object1所指向变量。

3、垃圾收集

一个类声明的两个对象如果具有相同的引用,两者就具有完全相同的实体,Java有垃圾收集机制,这种机制周期性的检测某个实体是否不在被任何对象引用,如果发现这样的实体,就释放实体所占用的内存。

参数传值

参数属于局部变量。方法被调用时,参数变量必须有具体的值

基本数据类型参数的传值

参数传递的值的级别不可以高于该参数的级别;例如:不可以向int型参数传递一个float值,但是可以向double型参数传递一个float级别

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

class Computer{
int add(int x,int y){
return x+y;
}
}

public class test1 {
public static void main(String []args){
Computer com =new Computer();
int m=100;int n=200;
int result =com.add(m,n);
System.out.println("m+n=:"+result);
}
}

引用类型参数的传值

Java的引用型数据包括数组,对象,接口

注:当参数是引用类型时,“传值”传递的是变量中存放的“引用”,而不是变量所引用的实体

可变参数

在声明方法时不给出参数列表中从某项开始直至最后一项参数的名字和个数,但这些参数的类型必须相同。可变参数使用“…”表示若干个参数

例如:

1
public void f(int ...x)

再如:

1
public void g(double a,int...x)

注:

1
public void method(int ...x,int y)//wrong

错误的,可变参数x即“参数代表”必须是参数列表中的最后一个。