跳转至

面向对象编程

Java 是一种面向对象的编程语言,程序的基本单位就是类,所有的代码必须在类的上下文中定义和执行。

/**
 * 特殊的多行注释
 * 需要写在类和方法的定义处,可以用于自动创建文档
 * @auther test
 */

public class Hello {
    /*
    Java 规定程序必须从 main() 开始
    而且必须是 static 静态的
    且参数必须是 String[] 数组
    void 为返回值类型,表示没有返回值
    */
    public static void main(String[] args) {
        System.out.println("Hello, world!");  // 代码行以分号结尾,打印一个字符串到屏幕上
    }
}

public 类的文件名必需保存为与类名完全一致的 Hello.java

javac Hello.java  # 先编译,生成 Hello.class
java Hello  # JVM 会自动查找与 Hello 对应的 .class 运行
'
Hello, world!
'

实例化 instance

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

// 定义一个引用类型的变量指向new创建的实例
Person zs = new Person();

zs.name = "Zhang San"; // 对字段name赋值
zs.age = 30; // 对字段age赋值
System.out.println(zs.name); // 访问字段name

package

一个类总是属于某个包,类名只是一个简写,真正的完整类名是:包名.类名,比如 java.util.Arrays

在 Java 虚拟机执行的时候,JVM 只看完整类名,因此,只要包名不同,类就不同。

. 表示不同层级,但没有继承关系,java.utiljava.util.zip 是不同的包,Java 文件对应的目录层次要和包的层次一致

为了避免名字冲突,我们需要确定唯一的包名,通常使用倒置的域名来确保唯一性,比如:com.{公司}.业务线.子业务线

JDK 的核心类 String, System, Runtime... 通常使用 java.lang 包,其他常用类定义在 java.util.*, java.text.*, java.math.*

// Main.java
package hello;  // 定义 class 的包名,如果没定义则使用默认包,但很容易引起名字冲突

public class Person {
    void hello() {
        System.out.println("Hello!");
    }
}

编写 class 的时候,编译器会自动帮我们做两个 import 动作:

  • 默认自动 import 当前 package 的其他 class
  • 默认自动 import java.lang.*

包引用的几种不同方式

// Main.java
package hello;

import java.text.Format;

public class Main {
    public static void main(String[] args) {
        // Main 和 Person 都位于同一个 package hello,可以互相访问包作用域的字段和方法
        Person p = new Person();
        p.hello();
        // 使用完整类名 -> java.util.List
        java.util.List list;
        // 使用import的类 -> java.text.Format
        Format format = null;
        // 使用java.lang包的String -> java.lang.String
        String s = "hi"; // ok,
        // 使用java.lang包的System -> java.lang.System
        System.out.println(s);

        // 编译错误:无法找到MessageFormat
        MessageFormat mf = null;  // MessageFormat cannot be resolved to a type
    }
}

修饰符

  • 无修饰符(默认),只能被被同 package 访问到
  • public 可以被任意包访问,一个 .java 文件只能包含一个 public 类,且 public 类必须和文件同名
  • final 修饰的类、方法、字段不可以被子类继承、覆写、重新赋值

仅用于非顶级类(即内层/嵌套类)的修饰符

  • private 无法被其他类以及子类访问,只允许类内部访问
  • protected 可以被继承树内的子类访问

构造方法

创建实例的时候,实际上是通过构造方法来初始化实例的

如果既要能使用带参数的构造方法,又想保留不带参数的构造方法,可以分别定义两个构造方法

public class Main {
    public static void main(String[] args) {
        Person p1 = new Person("Xiao Ming", 15); // 既可以调用带参数的构造方法
        Person p2 = new Person(); // 也可以调用无参数构造方法
    }
}

class Person {
    private String name;
    private int age;

    // 构造方法的名称就是类名,没有返回值(也没有void),调用构造方法,必须用new操作符
    // 任何类都有构造方法,没定义时编译器会自动生成一个没有参数,也没有执行语句的构造方法
    public Person() {
    }

    // 可以定义多个构造方法,在通过new操作符调用的时候,编译器通过构造方法的参数数量、位置和类型自动区分
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

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

方法重载

在一个类中,如果有功能类似的方法只是参数有所不同(返回值类型通常都是相同的),则可以把这一组方法名做成同名方法,即方法重载(Overload)

class Hello {
    public void hello() {
        System.out.println("Hello, world!");
    }

    public void hello(String name) {
        System.out.println("Hello, " + name + "!");
    }

    public void hello(String name, int age) {
        if (age < 18) {
            System.out.println("Hi, " + name + "!");
        } else {
            System.out.println("Hello, " + name + "!");
        }
    }
}


// String.indexOf()
// 可以通过传入不同的参数调用同名的不同方法
public class Main {
    public static void main(String[] args) {
        String s = "Test string";
        int n1 = s.indexOf('t');
        int n2 = s.indexOf("st");
        int n3 = s.indexOf("st", 4);
        System.out.println(n1);
        System.out.println(n2);
        System.out.println(n3);
    }
}

继承

除了 Object,所有类都有且只有一个父类,没有明确继承关系时,默认 extends Object

class Person {
    private String name;
    public String getName() {...}
    public void setName(String name) {...}

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

class Student extends Person {
    private int age;
    public int getAge() {...}
    public void setAge(int age) {...}

    // 在继承关系中,子类如果定义了一个与父类方法签名完全相同的方法,被称为覆写(Override)
    // 方法名,参数,返回值类型都必须相同
    @Override
    public void run() {
        System.out.println("Student.run");
    }

多态

多态是指,针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法。

// 引用变量的声明类型可能与其实际类型不符
// Java的实例方法调用是基于运行时的实际类型的动态调用,而非变量的声明类型,被称之为多态(Polymorphic)
// 比如一个实际类型为Student,引用类型为Person的变量,调用的其实是Student的run()方法
Person p = new Student();
p.run();

抽象类

由于多态的存在,每个子类都可以覆写父类的方法

如果父类的方法本身不需要实现任何功能,仅仅是为了定义方法签名,目的是让子类去覆写它,那么,可以把父类的方法声明为抽象方法

因为无法执行抽象方法,因此这个类也必须申明为抽象类

// 抽象类
abstract class Person {
    // 抽象方法
    public abstract void run();
    public abstract String getName();
}

抽象类无法被实例化,只能用于被继承,其子类必须实现其定义的抽象方法,否则编译会报错,因此抽象方法本质上形成了一种规范,保证所有子类都有相同的接口实现

接口

如果一个抽象类没有字段,所有方法全部都是抽象方法,就可以定义为比抽象类还要抽象的接口

// 类换成 interface
interface Person {
    // 方法去掉 public abstract
    void run();
    String getName();

    // 当我们需要给接口新增一个方法时,会涉及到修改全部子类
    // 如果新增的是default方法,那么子类可以不必覆写
    default void fly() {
        System.out.println(getName() + " fly");
    }
}

类需要使用 implements 关键字去实现一个 interface

class Student implements Person {
    private String name;

    // 构造方法
    public Student(String name) {
        this.name = name;
    }

    // 覆写
    @Override
    public void run() {
        System.out.println(this.name + " run");
    }

    // 覆写
    @Override
    public String getName() {
        return this.name;
    }
}

Java 中的类不能继承(extends)多个父类,但可以实现(implements)多个接口

class Student implements Person, Hello { // 实现了两个interface
    ...
}

一个接口还可以继承(extends)自另一个接口,相当于扩展了接口

interface Hello {
    void hello();
}

interface Person extends Hello {
    void run();
    String getName();
}

Inner Class

Java 的内部类可分为 Inner Class、Anonymous Class 和 Static Nested Class 三种

Inner Class 的实例不能单独存在,必须依附于一个 Outer Class 的实例

public class Main {
    public static void main(String[] args) {
        // 要实例化一个Inner,我们必须首先创建一个Outer的实例,然后调用Outer实例的new来创建Inner实例
        Outer outer = new Outer("Nested");  // 实例化一个Outer
        Outer.Inner inner = outer.new Inner();  // 实例化一个Inner
        inner.hello();
    }
}

class Outer {
    private String name;

    Outer(String name) {
        this.name = name;
    }

    class Inner {
        void hello() {
            System.out.println("Hello, " + Outer.this.name);
        }
    }
}

JavaBean

JavaBean 是 Java 语言中一种特殊的类,它遵循一套特定的命名和设计约定,使得它能够被工具(如 IDE、可视化构建工具或框架)轻松地操作、内省(Introspection)和重用

JavaBean 本身不包含业务逻辑,主要用于封装数据,因此它们常被称为数据模型(Data Model)或 POJO (Plain Old Java Object) 的一种特殊形式

// 通常需要实现可序列化 java.io.Serializable 接口,这允许 JavaBean 对象在网络上传输或持久化到磁盘上。
public class User implements java.io.Serializable {

    // 1. 私有属性
    private String name;
    private int age;
    private boolean active;

    // 2. 公有默认构造函数
    public User() {
        // 必须存在
    }

    // 3. 公共的 Getter 和 Setter 方法 (name)
    public String getName() {
        return name;
    }

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

    // 3. 公共的 Getter 和 Setter 方法 (age)
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    // 3. 布尔类型的 Getter (is)
    public boolean isActive() {
        return active;
    }

    public void setActive(boolean active) {
        this.active = active;
    }
}