文章目录
一、封装
1. 介绍
封装就是将对象的属性保护起来,只能通过类内部的方法来访问
可以在修改属性时做一些检验防止意外发生,提高安全性
封装好以后其他人只需要知道怎么调用,不用考虑内部的实现原理
2. 实现步骤
- 将属性私有化(
private
),使其不能被外部访问 - 设置公共的(
public
)set
方法来设置属性的值 - 设置公共的(
public
)get
方法来获取属性的值
3. IDEA快捷键
alt + insert
后选择getter和setter
4. 与构造器结合
将set
方法写在构造器中,可以保证初始化的安全
5. 示例
public class Homework {
private String password = "000000";
public Homework(String password) {
setPassword(password);
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
if(password.length() == 6){
this.password = password;
} else {
System.out.println("密码不是六位,默认密码为000000");
}
}
}
二、继承
1. 为什么需要继承
有些类中有很多相同的方法,但是又有一些不同的方法,这就造成了代码冗余。
将共同的部分抽离成父类(也叫基类,超类),让子类(也叫派生类)去继承可以解决这个问题。
2. 基础语法
使用extends
关键字
父类
public class Student {
public String name;
public int age;
private float score;
public float getScore() {
return score;
}
public void setScore(float score) {
this.score = score;
}
public void info() {
System.out.println("姓名:" + name + " 年龄:" + age + " 分数" + score);
}
}
子类
//让pupil类继承student类
public class Pupil extends Student{
public void testing() {
// 这里用到了从父类继承的name属性
System.out.println("小学生" + name + "正在考小学数学");
}
public static void main(String[] args) {
Pupil pupil = new Pupil();
pupil.name = "xiaoming";
pupil.testing();
}
}
3. 细节
- 子类继承了所有的属性和方法,但不一定能用。
私有属性和方法不能直接去访问,非私有的属性和方法可以在子类直接访问,要通过父类提供的公共的方法去访问。
这里的score是私有属性,可以看到直接使用IDEA会报错
2. 子类必须调用父类的构造器完成父类的初始化,因为子类中有一个隐藏的无参的super()
函数,super
函数在子类构造器的最上面,会被默认调用。
3. 创建子类对象时,无论子类使用哪个构造器,都会默认调用父类的无参构造器,如果父类没有无参构造器,则必须指明使用父类的哪个构造器。
4. 如果希望使用父类的某一个构造器,则需要显式得调用。
5. super
必须放在构造器的第一行。
6. super()
和this()
都只能放在第一行,因此两个方法不能共存。
7. 所有的类都是Object的子类,Object类是所有类的父类。(在IDEA中ctrl + H可以查看类之间的关系)。
- 父类构造器的调用不限于直接父类,将一直追溯至Object。
- 子类只能直接继承一个父类,java是单继承机制。
- 不能滥用继承,子类和父类之间必须满足is-a的逻辑关系。
4. 内存本质
5. super
5.1 基本介绍
super是父类的引用,类似于this是子类本身的引用。
使用super可以访问父类的属性、方法和构造器。
5.2 基本语法
- super.属性名可以访问父类的属性,但不能访问私有属性。
- super.方法名可以访问父类的方法,但不能访问私有方法。
- super(参数)可以访问父类的构造器,必须放在第一句。
5.3 细节
- 调用父类构造器的好处:分工明确,父类由父类构造器初始化,子类由子类构造器初始化。
- 当子类与父类的属性和方法重名时,必须通过super来访问直接父类的属性和方法,如果不同名使用super和使用this效果相同。
- super的访问不限于直接父类,也可以访问爷爷类的成员,如果父类和爷爷类中也有重名类,则遵循就近原则
5.4 super与this
6. 方法重写(override)
6.1 基本介绍
如果子类中的某个方法与父类(不一定是直接父类)中完全相同(方法名,返回类型,参数),则子类中的方法会覆盖掉从父类中继承来的方法。
6.2 细节
- 子类中重写的方法的返回类型可以是父类方法返回类型的的子类
(例如父类返回Object,则子类可以返回String)。 - 子类的参数和方法名要和父类完全相同才可以重写。
- 子类方法不能缩小父类方法的访问权限。
6.3 重写和重载
6.4 重写练习题
题目要求
代码
// 定义父类
class Person {
private String name;
private int age;
// 父类的构造器
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void say() {
System.out.println("我的名字是:" + name +
" 我的年龄是:" + age);
}
}
// 定义子类
class Student extends Person{
private String id;
private float score;
// 子类的构造器
public Student(String name, int age, String id, float score) {
// 调用父类的构造器
super(name, age);
this.id = id;
this.score = score;
}
public void say() {
// 调用父类的say方法
super.say();
System.out.println("我的学号是:" + id +
"我的分数是:" + score);
}
public static void main(String[] args) {
Student student = new Student("小明",18,"123456",99);
student.say();
}
}
三、多态
1. 基本介绍
建立在封装和继承之上。
指的是方法和对象具有多种形态,父类的引用可以指向子类。
看不懂没关系,往下继续看就明白了。
举一个动物给动物喂食的案例,不再需要每种动物都重载一次方法,直接使用动物的父类和食物的父类来指向各个子类。
之后再添加新的子类时也不需要修改feed方法。
2. 具体体现
2.1 方法的多态
重写和重载体现多态
2.2 方法的多态
- 一个对象的编译类型和运行类型可以不一样
- 编译类型在定义是就已经确定了,不能改变
- 运行类型是可以变化的
- 编译类型看等号的左边,运行类型看等号的右边
例如animal和cat是两个类
animal o = new cat();
定义的o编译类型为animal,运行类型为cat
3. 细节
3.1 前提条件
两个对象(类)存在继承关系
3.2 向上转型
- 本质: 父类的引用指向了子类的对象
- 语法:
父类 引用名 = new 子类()
- 特点:
- 编译类型看左边,运行类型看右边。
- 可以直接调用父类中的所有成员(需要遵守访问权限)。
- 不能调用子类中的特有成员,只能调用重写的成员,在编译阶段,能调用那些成员是由编译类型决定的。
- 运行效果看子类的具体实现,调用方法时是从运行类型开始查找。
3.3 向下转型
- 向下转型的目的是为了调用子类的特有成员。
- 语法:
子类 引用名= (子类类型)父类对象
- 只能强转父类的引用,不能强转父类的对象(向下转型的前提是向上转型,经历过向上转型的对象(引用)才可以被向下转型)
- 要求父类的引用必须指向当前目标类型的对象
- 可以调用子类的所有成员
3.4 属性的重写
- 属性没有重写和转型一说,属性的值直接看编译类型
instanceof
,可以查看一个对象是否是某个类型或者其子类型(运行类型)
例如:A instanceof B,检查A对象是否是B类型,或者A对象是否是B类型的子类型,返回布尔值
4. 应用场景
4.1 多态数组
- 定义:数组定义的类型为父类类型,实际存储的对象为子类类型。
- 数组中子类特有的方法可以通过向下转型调用。
- 可以使用instanceof关键字判断转型为哪个子类。
//Teacher类
public class Teacher extends Person{
public static void main(String[] args) {
// 向上转型 将Student和Teacher都存储在Person数组中
Person[] people = new Person[5];
people[0] = new Person("砂糖",12);
people[1] = new Student("刻晴",16,88.0);
people[2] = new Student("蜜莓",14,80.0);
people[3] = new Teacher("凯尔希",100,80000);
people[4] = new Teacher("钟离",5000,0);
for (int i = 0; i < people.length; i++) {
//instanceof 与 向下转型结合调用子类特有方法
if(people[i] instanceof Student){
Student student = (Student)people[i];
student.study();
} else if(people[i] instanceof Teacher){
Teacher teacher = (Teacher)people[i];
teacher.teach();
}
people[i].say(people[i].getAge(), people[i].getName());
}
}
private int money;
public Teacher(String name, int age, int money) {
super(name, age);
this.money = money;
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
@Override
public void say(int age, String name) {
super.say(age, name);
System.out.println("我的工资是:" + money);
}
public void teach(){
System.out.println(getName() + "正在讲课");
}
}
//Student 类
public class Student extends Person{
private double score;
public Student(String name, int age, double score) {
super(name, age);
this.score = score;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
public void say(int age, String name) {
super.say(age, name);
System.out.println("我的分数是:" + score);
}
public void study(){
System.out.println(getName() + "正在学习");
}
}
//Person 类
public class Person {
private String name;
private int age;
public Person(String name, int age) {
setAge(age);
setName(name);
}
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void say(int age,String name) {
System.out.println("我的名字是:" + name + "我的年龄是" + age);
}
4.2 多态参数
方法定义的形参为父类,实参可以是子类。
//test类
public class test {
public static void main(String[] args) {
sass tom = new sass("tom", 2000);
less milan = new less("milan", 5000, 200000);
test test = new test();
test.show(tom);
test.show(milan);
test.testwork(tom);
test.testwork(milan);
}
public void show(css e){
System.out.println(e.getMoney());
}
// 形参参数为css父类,传入的实参为其子类
public void testwork(css e){
if(e instanceof less){
((less)e).manage();
} else if(e instanceof sass){
((sass)e).work();
} else {
System.out.println("自己检查哪里出错了");
}
}
}
//css类
public class css {
String name;
private int money;
public css(String name, int money) {
this.name = name;
this.money = money;
}
public int getMoney() {
return money * 12;
}
}
//sass类
public class sass extends css{
public void work(){
System.out.println("员工在工作");
}
public sass(String name, int money) {
super(name, money);
}
//方法重写
@Override
public int getMoney() {
return super.getMoney();
}
}
// less类
public class less extends css {
int bonus = 1000;
public less(String name, int money, int bonus) {
super(name, money);
this.bonus = bonus;
}
public void manage(){
System.out.println("领导在管理");
}
@Override
public int getMoney() {
return super.getMoney() + bonus;
}
}