面向对象三大基本特征:封装、继承、多态
一、封装
为什么要有封装?
保证数据的安全性。
将类的信息全部封装到内部,让外部无法直接访问,确保数据的安全。
- 1、将属性私有化(private)
- 2、通过间接的方式让外部访问内部的私有化属性(setter方法)
java
public class Person {
//成员变量
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
if(age < 0){
System.out.println(("年龄不能小于0,已重新赋值"));
this.age = 1;
}else{
this.age = age;
}
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
java
public class Main {
public static void main(String[] args) {
Person person = new Person();
person.setAge(-10);
person.setName("张三");
System.out.println(person);
}
}

1.static关键字
static表示静态或者全局,可以用来修饰成员变量和成员方法以及代码块。
成员变量和成员方法,必须通过对象来访问,必须依赖于对象。
而使用static修饰的成员变量和成员方法独立于该类任何一个实例对象,访问时不依赖任何一个对象,可以直接通过类来访问。
被static修饰的变量叫静态变量,被static修饰的方法叫做静态方法。
多个对象共享,内存中只有一份。
java
public class User {
public static String name;
public static void show(){
System.out.println(("这是一个User对象"));
}
}
java
public class UserTest {
public static void main(String[] args) {
User.name = "张三";
System.out.println(User.name);
User.show();
}
}
使用static修饰的静态方法不能使用this关键字,不能直接访问类的实例变量和实例方法,可以直接访问类的静态变量和静态方法,如果要访问实例变量和实例方法,必须先实例化该类的对象,通过对象去访问。
java
public class Example {
int instanceVar = 10; // 实例变量
static int staticVar = 20; // 静态变量
public static void staticMethod() {
// ❌ 编译错误:无法从静态上下文中引用非静态变量
System.out.println(instanceVar);
// ❌ 编译错误:无法从静态上下文中引用非静态方法
instanceMethod();
// ❌ 编译错误:无法从静态上下文中引用 this
System.out.println(this);
// ✅ 正确:直接访问静态成员
System.out.println(staticVar);
}
public void instanceMethod() {
System.out.println("实例方法");
}
}
2.如何访问实例成员?
必须先实例化对象,通过对象引用访问:
java
public static void staticMethod() {
// 创建对象
Example obj = new Example();
// ✅ 现在可以通过对象访问实例成员
System.out.println(obj.instanceVar); // 输出 10
obj.instanceMethod(); // 调用实例方法
}
static还可以修饰代码块,被static修饰的代码块叫做静态代码块。
只执行一次,当类被加载到内存中的时候,会自动执行静态代码块,不需要手动调用。
java
public class User {
static int num;
static{
num++;
System.out.println("执行了静态代码块");
}
}
java
public class UserTest {
public static void main(String[] args) {
User user = new User();
User user1 = new User();
User user2 = new User();
System.out.println((user.num));
System.out.println((user1.num));
System.out.println((user2.num));
}
}

程序执行的时候,首先会将程序中用到的类加载到内存中,并且只加载一次,加载的时候就会自动执行静态代码块,因为只加载一次,所以静态代码块只执行一次。
静态代码块只执行一次,当程序加载类的时候执行。
代码块执行多次,取决于创建了多少个对象。程序创建对象的时候执行,创建一个就执行一次。
先加载类,再创建对象,调用构造器之前,先执行代码块。
java
public class User {
{
System.out.println("执行了代码块");
}
static{
System.out.println("执行了静态代码块");
}
}
java
public class UserTest {
public static void main(String[] args) {
User user = new User();
User user1 = new User();
User user2 = new User();
}
}

3.代码块、静态代码块、父子类的构造器的执行顺序
- 调用父类的静态代码块;
- 调用子类的静态代码块;
- 调用父类的代码块;
- 调用父类的构造器
- 调用子类的代码块;
- 调用子类的构造器。
二、继承
Java代码复用的重要特征,子类可以直接继承父类全部非私有信息(属性、方法)
创建子类对象时,JVM 只创建一个对象,但会先执行父类构造器来初始化继承的成员。父类构造器执行完毕,子类构造器继续完成剩余初始化。
1.子类访问父类
创建子类对象的时候,无论使用无参还是有参,都会默认使用无参创建父类对象。
java
public class People {
public People(){
System.out.println("调用无参构造创建People对象");
}
public People(int id){
System.out.println("调用有参构造创建People对象");
}
}
java
public class Teacher extends People{
public Teacher(){
System.out.println("调用无参构造创建Teacher对象");
}
public Teacher(int id){
System.out.println("调用有参构造创建Teacher对象");
}
}
java
public class Test2 {
public static void main(String[] args) {
Teacher teacher = new Teacher();
Teacher teacher1 = new Teacher(1);
}
}

上述创建了6个对象,People的父类是Object
super();相当于调用父类的无参构造。
2.this();和super();
调用构造函数,必须写到第一行,且不能同时出现。
3.类的访问权限
访问权限修饰符
4种:public private 默认(不写)、protected
|-----------|------|-------|-------|-------|
| 修饰符 | 同一个类 | 同包不同类 | 不同包 | 不同包子类 |
| public | 可以访问 | 可以访问 | 可以访问 | 可以访问 |
| protected | 可以访问 | 可以访问 | 不可以访问 | 可以访问 |
| 默认 | 可以访问 | 可以访问 | 不可以访问 | 不可以访问 |
| private | 可以访问 | 不可以访问 | 不可以访问 | 不可以访问 |
4.方法重写
子类继承父类方法的基础上,对父类方法重新定义并覆盖的操作叫做方法重写。
方法重写的规则:
- 父子类方法名相同
- 父子类的参数列表相同
- 子类方法的返回值和父类方法的返回值相同或是其子类
- 子类方法的访问权限不能小于父类
private修饰的父类方法子类是无法继承的,更不能重写
5.方法重写和重载的区别?
|----|------|-----|------|---------|--------|
| 类型 | 所在位置 | 方法名 | 参数列表 | 返回值 | 访问权限 |
| 重写 | 父子类 | 相同 | 相同 | 相同或是其子类 | 不能小于父类 |
| 重载 | 同一个类 | 相同 | 不同 | 没有要求 | 没有要求 |
三、多态
1.多态的概念及例子
多态是指一个事物有多种表现形态(抽象的)。
定义一个类,该类的实例化对象在不同的业务场景中根据不同的需求呈现出不同的业务逻辑。
例:普通会员买书 9折优惠
超级会员买书 6折优惠
会员->收银员->超级会员
直接定义"会员",可以是普通会员、超级会员、VIP会员
从效果来看提高了代码的扩展性
使用的是多态编程思想,具体是通过继承来实现。
java
public class Member {
public void BuyBook(){
System.out.println("父类会员的方法");
}
}
java
public class OrdinaryMember extends Member{
public void BuyBook(){
System.out.println("普通会员买书打9折");
}
}
java
public class SuperMember extends Member {
public void BuyBook(){
System.out.println("超级会员买书打6折");
}
}
java
public class Cashier {
private Member member;
public Member getMember() {
return member;
}
public void setMember(Member member) {
this.member = member;
}
public void settlement(){
System.out.println("开始结算");
this.member.BuyBook();
}
}
java
public class BuyBookTest {
public static void main(String[] args) {
OrdinaryMember ordinaryMember = new OrdinaryMember();
SuperMember superMember = new SuperMember();
Cashier cashier = new Cashier();
cashier.setMember(superMember);
cashier.settlement();
}
}

2.多态的使用
- 定义方法时形参为父类,调用方法时传入的参数为子类对象。
java
public void setMember(Member member) {
this.member = member;
}
Cashier cashier = new Cashier();
cashier.setMember(superMember);
- 定义方法时返回值的数据类型为父类,调用方法时返回子类对象。
java
public Member getMember(String name){
if(name.equals("ordinaryMember")){
return new OrdinaryMember();
}
if(name.equals("superMember")){
return new SuperMember();
}
return null;
}
上述两种多态的形式,都是基于子类对象可以直接赋值给父类对象,自动类型转换。