面向对象编程¶
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.util 和 java.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;
}
}