『JavaSE』面向对象三大特征

Java面向对象的三大特征:封装、继承、多态。

封装

Java中的封装是指一个类把自己内部的实现细节进行隐藏,只暴露对外的接口(setter和getter方法)。封装又分为属性的封装和方法的封装。把属性定义为私有的,它们通过setter和getter方法来对属性的值进行设定和获取。

封装的思想保证了类内部数据结构的完整性,使用户无法轻易直接操作类的内部数据,这样降低了对内部数据的影响,提高了程序的安全性和可维护性。
封装的目的:
只能通过规定的方法访问数据
隐藏类的实现细节
方便加入控制语句
方便实现修改

public class Person {
    private int id;
    private String name;
    private Person person;
 
    public int getId() {
        return id;
    }
 
    public String getName() {
        return name;
    }
 
    public Person getPerson() {
        return person;
    }
 
    public void setId(int id) {
        this.id = id;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public void setPerson(Person person) {
        this.person = person;
    }
}

private实现封装
private/ public 这两个关键字表示 “访问权限控制” .

被 public 修饰的成员变量或者成员方法, 可以直接被类的调用者使用.
被 private 修饰的成员变量或者成员方法, 不能被类的调用者使用.

换句话说, 类的使用者根本不需要知道, 也不需要关注一个类都有哪些 private 的成员. 从而让类调用者以更低的成本来使用类.

直接使用 public

class Person {
	public String name = "张三";
	public int age = 18;
}
class Test {
	public static void main(String[] args) {
		Person person = new Person();
		System.out.println("我叫" + person.name + ", 今年" + person.age + "岁");
	}
}
// 执行结果
我叫张三, 今年18

这样的代码导致类的使用者(main方法的代码)必须要了解 Person 类内部的实现, 才能够使用这个类. 学习成本较高
一旦类的实现者修改了代码(例如把 name 改成 myName), 那么类的使用者就需要大规模的修改自己的代码, 维护成本较高

使用 private 封装属性, 并提供 public 方法供类的调用者使用

class Person {
	private String name = "张三";
	private int age = 18;
	public void show() {
		System.out.println("我叫" + name + ", 今年" + age + "岁");
	}
}
class Test {
	public static void main(String[] args) {
		Person person = new Person();
		person.show();
	}
}
// 执行结果
我叫张三, 今年18

此时字段已经使用 private 来修饰. 类的调用者(main方法中)不能直接使用. 而需要借助 show 方法. 此时类的使用者就不必了解 Person 类的实现细节.
同时如果类的实现者修改了字段的名字, 类的调用者不需要做出任何修改(类的调用者根本访问不到 name, age这样的字段).

注意事项:
①private 不光能修饰字段, 也能修饰方法
②通常情况下我们会把字段设为 private 属性, 但是方法是否需要设为 public, 就需要视具体情形而定. 一般我们希望一个类只提供 “必要的” public 方法, 而不应该是把所有的方法都无脑设为 public.

getter和setter方法

当我们使用 private 来修饰字段的时候, 就无法直接使用这个字段了.
代码示例:

class Person {
	private String name = "张三";
	private int age = 18;
	public void show() {
		System.out.println("我叫" + name + ", 今年" + age + "岁");
	}
}
class Test {
	public static void main(String[] args) {
		Person person = new Person();
		person.age = 20;
		person.show();
	}
}
// 编译出错
Test.java:13: 错误: age可以在Person中访问private
		person.age = 20;
			^
1 个错误

此时如果需要获取或者修改这个 private 属性, 就需要使用 getter / setter 方法.
代码示例:

class Person {
	private String name;//实例成员变量
	private int age;
	public void setName(String name){
		//name = name;//不能这样写
		this.name = name;//this引用,表示调用该方法的对象
	}
	public String getName(){
		return name;
	}
	public void show(){
		System.out.println("name: "+name+" age: "+age);
	}
}

public static void main(String[] args) {
	Person person = new Person();
	person.setName("caocao");
	String name = person.getName();
	System.out.println(name);
	person.show();
}
// 运行结果
caocao
name: caocao age: 0

注意事项

<1> getName 即为 getter 方法, 表示获取这个成员的值.
<2> setName 即为 setter 方法, 表示设置这个成员的值.
<3> 当set方法的形参名字和类中的成员属性的名字一样的时候,如果不使用this, 相当于自赋值. this 表示当前实例的引用.
<4> 不是所有的字段都一定要提供 setter / getter 方法, 而是要根据实际情况决定提供哪种方法.
<5> 在 IDEA 中可以使用 alt + insert (或者 alt + F12) 快速生成 setter / getter 方法.在 VSCode 中可以使用鼠标右键菜单 -> 源代码操作 中自动生成 setter / getter 方法.

字段 vs 属性
我们通常将类的数据成员称为 “字段(field)” , 如果该字段同时提供了 getter / setter 方法, 那么我们也可以将它称为 “属性(property )”.

继承

继承就是子类继承父类的特征和行为,使得子类对象具有父类的信息,同时可以扩展。基本语法:

class 子类 extends 父类 {
}

a.使用 extends 指定父类.
b.Java 中一个子类只能继承一个父类 (而C++/Python等语言支持多继承).单继承多实现。
c.子类会继承父类的所有 public 的字段和方法.
d.对于父类的 private 的字段和方法, 子类中是无法访问的.
e.子类的实例中, 也包含着父类的实例. 可以使用 super 关键字得到父类实例的引用
f. 子类可以拥有属于自己的属性和方法

子类访问父类成员:
父类无参的构造方法:super();
父类有参的构造方法:super(name);
访问父类属性:super.name;
访问父类方法:super.方法名();

子类不能被继承的父类成员:
构造方法
private成员
子类与父类不在同包,使用默认访问权限的成员

class Animal {
	public String name;
	public Animal(String name) {
		this.name = name;
	}
	public void eat(String food) {
		System.out.println(this.name + "正在吃" + food);
	}
}
class Cat extends Animal {
	public Cat(String name) {
		// 使用 super 调用父类的构造方法.
		super(name);
	}
}
class Bird extends Animal {
	public Bird(String name) {
		super(name);
	}
	public void fly() {
		System.out.println(this.name + "正在飞 ︿( ̄︶ ̄)︿");
	}
}
public class Test {
	public static void main(String[] args) {
		Cat cat = new Cat("小黑");
		cat.eat("猫粮");
		Bird bird = new Bird("圆圆");
		bird.fly();
	}
}

protected 关键字
如果把字段设为 private, 子类不能访问. 但是设成 public, 又违背了我们 “封装” 的初衷.两全其美的办法就是 protected 关键字.对于类的调用者来说, protected 修饰的字段和方法是不能访问的.对于类的子类和同一个包的其他类来说, protected 修饰的字段和方法是可以访问的.

// Animal.java
public class Animal {
	protected String name;
	public Animal(String name) {
		this.name = name;
	}
	public void eat(String food) {
		System.out.println(this.name + "正在吃" + food);
	}
}
// Bird.java
public class Bird extends Animal {
	public Bird(String name) {
		super(name);
	}
	public void fly() {
		// 对于父类的 protected 字段, 子类可以正确访问
		System.out.println(this.name + "正在飞 ︿( ̄︶ ̄)︿");
	}
}
// Test.java 和 Animal.java 不在同一个包中了.
public class Test {
	public static void main(String[] args) {
		Animal animal = new Animal("小动物");
		System.out.println(animal.name); // 此时编译出错, 无法访问 name
	}
}

小结: Java 中对于字段和方法共有四种访问权限
private: 类内部能访问, 类外部不能访问
默认(也叫包访问权限): 类内部能访问, 同一个包中的类可以访问, 其他类不能访问.
protected: 类内部能访问, 子类和同一个包中的类可以访问, 其他类不能访问.
public : 类内部和类的调用者都能访问

在这里插入图片描述
final 关键字

final 关键字, 修饰一个变量或者字段的时候, 表示常量 (不能修改)

final int a = 10;
a = 20; // 编译出错

final 关键字也能修饰类, 此时表示被修饰的类就不能被继承.

final public class Animal {
	...
}
public class Bird extends Animal {
	...
}
// 编译出错
Error:(3, 27) java: 无法从最终com.bit.Animal进行继承

final 关键字的功能是 限制 类被继承
“限制” 这件事情意味着 “不灵活”. 在编程中, 灵活往往不见得是一件好事. 灵活可能意味着更容易出错.
是用 final 修饰的类被继承的时候, 就会编译报错, 此时就可以提示我们这样的继承是有悖这个类设计的初衷的.
我们平时是用的 String 字符串类, 就是用 final 修饰的, 不能被继承.

super关键字

代码中由于使用了重写机制, 调用到的是子类的方法. 如果需要在子类内部调用父类方法可以使用super 关键字。

super只能出现在子类的方法和构造方法中;
super调用构造方法时,只能是第一句;
super不能访问法父类的private成员;

注意 super 和 this 功能有些相似, 但是还是要注意其中的区别

在这里插入图片描述

多态

多态就是统一接口,使用不同的实现,而执行不同的操作。
多态的实现方式: 静态多态(重载)、动态多态(重写)
重写和重载的区别:
重写:把在子类中把父类本身有的方法重新写一遍。
重载:在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同甚至是参数顺序不同)则视为重载。

在这里插入图片描述

向上转型和向下转型:

向上转型: 父类引用指向子类对象
父类 = new 子类();
向下转型: 子类引用指向父类对象
父类 对象1 = new 子类();
子类 对象2 = (子类)对象1;

向上转型:父类引用指向子类对象,将会丢失子类和父类中不一致的方法,并且父类引用调用的变量只能是父类中的。

class Person{
    private String name = "Person";
    int age=0;
    public void function() {
		System.out.println("this is person");
	}
}
public class Child extends Person{
	
    public String grade;
    int age=10;
    public void function() {
		System.out.println("this is child");
	}
    public void b1(){
    	System.out.println("子类新方法");
    } //B类定义了自己的新方法
    @Test
    public  void test(){
        Person p = new Child();
        p.function();
        System.out.println(p.age);
    	
    }
}
//输出:this is child”  
0

并且我们是不能使用p.b1()的,编译错误,因为,此时该对象p已经失去了该方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值