JAVA笔记3——面向对象(上)

目录

一、面向对象的三大特性——封装、继承和多态 

1. 封装

2. 继承

3. 多态

二、类与对象 

1、对象的创建与使用 

 2、对象的引用传递

3、访问控制权限 

 4、封装性

​编辑

思考: 对于上面代码中的getName方法和getAge方法,去掉后不影响结果的输出,那么他们的存在是否多余?

回答: 不多余。理由如下:

1. 封装原则

2. 未来的扩展性

3. 代码的可维护性

4. 统一的访问方式

5. 代码的可读性

总结

 5、构造方法

5-1、定义构造方法

 5-2、构造方法的重载

6、this关键字 (标识成员变量)

6-1、调用本类中的属性 

 6-2、调用成员方法

 6-3、调用构造方法

7、代码块

7-1、普通代码块 

7-2、构造块 


一、面向对象的三大特性——封装、继承和多态 

Java 面向对象的三大特性是 封装继承多态。下面用通俗易懂的语言来解释它们:

1. 封装

封装 就像把复杂的东西藏起来,只暴露必要的部分。就好比你用遥控器开电视,你只需要按下按钮,电视就会开,但你不需要知道遥控器内部是怎么工作的。

通俗解释

  • 就像一个汉堡包,你只需要知道它是汉堡,可以吃,但你不需要知道里面的肉饼是怎么做的。

  • 在 Java 中,封装是把数据(变量)和操作数据的方法(函数)打包在一起,隐藏内部细节,只提供简单的接口供外界使用。

2. 继承

继承 就像“复制粘贴”,但更高级。它允许一个类(子类)继承另一个类(父类)的特性,就像儿子继承了父亲的财产,同时可以有自己的新东西。

通俗解释

  • 就像一个家族,父亲是 Animal,儿子是 DogDog 继承了 Animal 的特性(比如会叫、会跑),但 Dog 还有自己的特性(比如会摇尾巴)。

  • 在 Java 中,子类可以继承父类的属性和方法,减少重复代码。

3. 多态

多态 是“一个接口,多种实现”。就像一个按钮,按下去可能有不同的效果,具体取决于它在什么设备上。比如,手机上的按钮可能打开应用,而电视上的按钮可能调高音量。

通俗解释

  • 就像一个遥控器,上面有一个“播放”按钮。你按这个按钮时,不同的设备(电视、音响、DVD)会有不同的反应,但它们都实现了“播放”这个功能。

  • 在 Java 中,多态允许一个方法在不同的对象上有不同的表现形式。

总结一下:

  • 封装:隐藏复杂性,只暴露简单的接口。

  • 继承:复制父类的特性,同时可以扩展自己的功能。

  • 多态:一个接口,多种实现,让代码更灵活。

二、类与对象 

1、对象的创建与使用 

示例代码

class Student {

    String name;
    void read(){
        System.out.println("大家好,我是"+name+"我在看书!");
    }
}

public class InnerClassDemoTest {
    public static void main(String[] args){
        Student stu = new Student();//创建一个Student对象
        stu.name="zhangsan";//为stu对象的name属性赋值
        stu.read();//调用对象的方法
    }
    
}

输出

 2、对象的引用传递

类属于引用数据类型,引用数据类型的内存空间可以同时被多个栈内存引用。

代码示例

class Student {
    String name; // 声明 name 属性
    int age;     // 声明 age 属性

    void read() {
        System.out.println("大家好,我是" + name + ",年龄" + age);
    }
}

class Example02 {
    public static void main(String[] args) {
        Student stu1 = new Student(); // 创建 stu1 对象并实例化
        Student stu2 = null;         // 创建 stu2 对象,但不对其进行实例化
        stu2 = stu1;                 // stu1 给 stu2 分配空间使用权
        stu1.name = "小明";          // 为 stu1 对象的 name 属性赋值
        stu1.age = 20;
        stu2.age = 50;
        stu1.read();                 // 调用对象的方法
        stu2.read();
    }
}

输出

 stu1对象给stu2对象分配了空间使用权,二者指向同一个堆内存,所以stu2修改属性的值也会影响到stu1。

        所谓的引用传递,就是将一个堆内存的空间使用权分配给多个栈内存使用,每个栈内存空间都可以修改堆内存空间中的内容。 

注意:一个栈内存空间只能指向一个堆内存空间。如果想要再指向其他堆内存空间,就必须先断开已有的指向,才能分配新的指向。 

3、访问控制权限 

public class Test {
    public int aa;       // aa 可以被所有的类访问
    protected boolean bb; // bb 可以被所有子类以及本包的类访问
    void cc() {          // 默认访问权限,能在本包内访问
        System.out.println("包访问权限");
    }
    // private 权限的内部类,即私有的类,只能在本类中访问
    private class InnerClass {
    }
}

需要注意的是,外部类的访问权限只能是public或default,所以Test类只能使用public修饰或者不写修饰符 。    局部变量是没有访问权限的(即不可以设置访问权限),因为局部成员只在其所在的作用域内起作用,不可能被其它类访问。

局部变量:定义在方法中的变量 

小提示:Java程序的文件名
        如果一个源文件中定义的所有类都没有使用 public 修饰,那么这个源文件的文件名可以是一切合法的文件名;如果一个源文件中定义了一个使用 public 修饰的类,那么这个源文件的文件名必须与 public 修饰的类名相同。

 4、封装性

示例代码 

// 定义一个名为 Student 的类,用于表示学生的信息
class Student {
    // 定义一个私有字符串变量 name,用于存储学生的名字
    private String name;
    // 定义一个私有整型变量 age,用于存储学生的年龄
    private int age;

    // 定义一个公共方法 getName(),用于获取学生的姓名
    // 这是一个 getter 方法,返回 name 的值
    public String getName() {
        return name;
    }

    // 定义一个公共方法 setName(String name),用于设置学生的姓名
    // 这是一个 setter 方法,将传入的参数 name 赋值给类中的 name 变量
    public void setName(String name) {
        this.name = name; // this 关键字表示当前对象的 name 属性
    }

    // 定义一个公共方法 getAge(),用于获取学生的年龄
    // 这是一个 getter 方法,返回 age 的值
    public int getAge() {
        return age;
    }

    // 定义一个公共方法 setAge(int age),用于设置学生的年龄
    // 这是一个 setter 方法,但加入了逻辑判断,确保年龄是合法的
    public void setAge(int age) {
        // 如果传入的 age 小于 0,则输出错误提示
        if (age < 0) {
            System.out.println("您输入的年龄有误!");
        } else {
            // 如果年龄合法,则将传入的 age 赋值给类中的 age 变量
            this.age = age;
        }
    }

    // 定义一个公共方法 read(),用于输出学生的信息
    // 这个方法没有参数,也没有返回值
    public void read() {
        // 输出学生的姓名和年龄
        System.out.println("大家好,我是" + name + ", 年龄" + age);
    }
}

// 定义一个公共类 InnerClassDemoTest,作为程序的入口
public class InnerClassDemoTest {
    // main 方法是程序的入口点
    public static void main(String[] args) {
        // 创建一个 Student 类的对象,命名为 stu
        Student stu = new Student();

        // 调用 setName 方法,将学生的名字设置为 "zhangsan"
        stu.setName("zhangsan");

        // 调用 setAge 方法,尝试将学生的年龄设置为 -18
        // 由于 -18 小于 0,setAge 方法会输出错误提示
        stu.setAge(-18);

        // 调用 read 方法,输出学生的信息
        // 由于年龄设置失败,age 的值仍然是默认值 0
        stu.read();
    }
}

输出

思考: 对于上面代码中的getName方法和getAge方法,去掉后不影响结果的输出,那么他们的存在是否多余?

回答: 不多余。理由如下:

去掉 getName() 方法确实可以在当前代码中运行,但它的存在是为了遵循面向对象编程的 封装原则,并且为未来的扩展和维护提供便利。即使当前代码中没有用到 getName(),它的存在仍然是有意义的。

以下是一些具体的理由:

1. 封装原则

封装是面向对象编程的核心思想之一,它的目的是将类的内部实现细节隐藏起来,只暴露必要的接口给外部。通过 getName() 方法,我们控制了对 name 属性的访问,而不是直接暴露 name。即使现在看起来它只是简单地返回值,但这种设计为未来可能的修改提供了灵活性。

2. 未来的扩展性

即使现在 getName() 看起来是多余的,但如果未来需要对 name 的访问进行额外的逻辑处理(比如验证、格式化等),直接修改 getName() 就可以实现,而不需要修改调用它的代码。例如:

public String getName() {
    return name.toUpperCase(); // 返回大写的姓名
}

如果直接访问 name,这种逻辑就无法实现。

3. 代码的可维护性

在大型项目中,代码可能会被多个模块或开发者使用。如果直接访问 name,那么任何对 name 的修改都会影响到所有直接访问它的代码。而通过 getName(),你可以集中管理对 name 的访问逻辑,减少维护成本。

4. 统一的访问方式

在 Java 中,通常会为每个属性提供 getter 和 setter 方法,这是一种通用的编程规范。即使现在 getName() 没有实际作用,但它的存在让代码更符合规范,便于其他开发者理解和维护。

5. 代码的可读性

使用 getName() 方法可以让代码更清晰,尤其是当类中有多个属性时。例如:

System.out.println("大家好,我是" + getName() + ", 年龄" + getAge());

这种方式比直接访问 nameage 更符合面向对象编程的风格。

总结

虽然在当前的代码中去掉 getName() 不会影响运行,但它的存在是为了遵循封装原则、提高代码的可维护性、扩展性和可读性。如果你现在去掉 getName(),虽然代码可以运行,但会失去这些潜在的好处。

 5、构造方法

5-1、定义构造方法

无参构造方法示例 

class Student {
    public Student() { // 构造方法
        System.out.println("调用了无参构造方法");
    }
}

class Example05 {
    public static void main(String[] args) {
        System.out.println("声明对象..."); 
        Student stu = null; // 声明对象
        System.out.println("实例化对象..."); 
        stu = new Student(); // 实例化对象
    }
}

输出

 在实例化对象时,调用了无参构造方法

  有参构造方法

定义有参构造方法,可实现对属性的赋值

// 定义一个名为 Student 的类,用于表示学生的信息
class Student {
    // 定义一个私有字符串变量 name,用于存储学生的名字
    private String name;
    // 定义一个私有整型变量 age,用于存储学生的年龄
    private int age;

    // 定义一个有参构造方法,用于初始化 Student 对象的属性
    public Student(String n, int a) {
        // 将传入的参数 n 赋值给类中的 name 属性
        name = n;
        // 将传入的参数 a 赋值给类中的 age 属性
        age = a;
        // 输出一条消息,表示有参构造方法被调用
        System.out.println("调用了有参构造方法");
    }

    // 定义一个公共方法 read(),用于输出学生的信息
    public void read() {
        // 输出学生的姓名和年龄
        System.out.println("我是:" + name + ", 年龄:" + age);
    }
}

// 定义一个名为 Example05 的类,作为程序的入口
class Example05 {
    // main 方法是程序的入口点
    public static void main(String[] args) {
        // 创建一个 Student 类的对象,命名为 stu
        // 调用有参构造方法,传入 "张三" 和 18 作为参数
        Student stu = new Student("张三", 18);

        // 调用 stu 对象的 read() 方法,输出学生的信息
        stu.read();
    }
}

 输出

 5-2、构造方法的重载

示例代码 

class Student {
    private String name; // 声明私有属性 name
    private int age;     // 声明私有属性 age

    // 无参构造方法
    public Student() { }

    // 一个参数的构造方法
    public Student(String n) {
        name = n;
        System.out.println("调用了一个参数的构造方法");
    }

    // 两个参数的构造方法
    public Student(String n, int a) {
        name = n;
        age = a;
        System.out.println("调用了两个参数的构造方法");
    }

    // 读取学生信息的方法
    public void read() {
        System.out.println("我是:" + name + ",年龄:" + age);
    }
}

class Example07 {
    public static void main(String[] args) {
        Student stu  = new Student("张三"); // 实例化对象,调用一个参数的构造方法
        Student stu2 = new Student("张三", 18); // 实例化对象,调用两个参数的构造方法
        stu.read(); // 调用对象的 read 方法
        stu2.read(); // 调用对象的 read 方法
    }
}

 输出

 注意:一旦为类定义了构造方法,系统就不再提供默认的构造方法了。

        在一个类中如果定义了有参构造方法,最好在构建一个无参构造方法。

        另外,构造方法通常使用public 进行修饰

6、this关键字 (标识成员变量)

当成员变量与局部变量发生重名问题时,需要使用this这个关键字来分辨。

6-1、调用本类中的属性 

代码示例 

class Student {
    private String name;
    private int age;
    // 定义构造方法
    public Student(String name, int age) {
        name = name;
        age = age;
    }
    public String read() {
        return "我是:" + name + ",年龄:" + age;
    }
}

public class Example09 {
    public static void main(String[] args) {
        Student stu = new Student("张三", 18);
        System.out.println(stu.read());
    }
}

输出

从输出中可以看到,成员变量并没有被赋值成功 ,因为成员变量与局部变量重名,编译器无法识别。

将构造方法中的代码改为         this.name = name;
                                                this.age = age; 

输出

 当局部变量与成员变量相同时,需要使用this关键字标识成员变量,否则编译器无法确定那个名称是当前对象的成员。

 6-2、调用成员方法

public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    // 成员方法 1:sayHello()
    public void sayHello() {
        System.out.println("Hello, my name is " + name);
    }

    // 成员方法 2:greet()
    public void greet() {
        // 使用 this 调用 sayHello() 方法
        this.sayHello();
        System.out.println("Nice to meet you!");
    }

    public static void main(String[] args) {
        Person person = new Person("Alice");
        person.greet(); // 输出:Hello, my name is Alice 和 Nice to meet you!
    }
}

输出

 6-3、调用构造方法

代码示例 

public class Person {
    private String name;
    private int age;

    // 无参构造方法
    public Person() {
        this("Unknown", 0); // 调用带参数的构造方法
        System.out.println("无参构造方法被调用__");
    }

    // 带参数的构造方法
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("带参数构造方法被调用");
    }

    public static void main(String[] args) {
        Person p1 = new Person(); // 调用无参构造方法
        Person p2 = new Person("Alice", 25); // 调用带参数的构造方法
    }
}

输出

7、代码块

代码块,简单来讲就是{}括起来的一段代码

7-1、普通代码块 

上述代码中,每一对“{}”括起来的代码都称为一个代码块 

7-2、构造块 

构造块,是指在类中定义的代码块 

class Student {
    String name; // 成员属性
    {
        System.out.println("我是构造块"); // 与构造方法同级
    }
    // 构造方法
    public Student() {
        System.out.println("我是 Student 类的构造方法");
    }
}

public class Example12 {
    public static void main(String[] args) {
        Student stu1 = new Student();
        Student stu2 = new Student();
    }
}

 输出

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值