总之,封装、继承和多态是面向对象编程的三大基本特征,它们使得程序更加模块化、可维护性和可重用性更高。
1.隐藏实现细节,只管调用
2.可以对数据进行验证,保证安全合理 (private 在同类可改)
package com.hspedu;
public class Encapsulation01 {
public static void main(String[] args) {
Person person = new Person();
person.setName("jack8080");
person.setAge(300);
person.setSalary(30000);
System.out.println(person.info());
}
}
/*
* 要求:年龄,工资为隐私
* 数据验证:年龄(1-120) 不合理给默认年龄
* name 2-6个字符
* */
class Person {
public String name;
private int age;
private double salary;
//使用快捷键
public String getName() {
return name;
}
public void setName(String name) {
if(name.length() >= 2 && name.length() <= 6){
this.name = name;
}else {
System.out.println("name 需要在2-6个字符");
this.name = "无名人";
}
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age >= 1 && age <= 120){
this.age = age;
} else {
System.out.println("输入年龄需要在 1-120");
this.age = 18; //给默认年龄
}
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
//写一个方法,返回属性信息
public String info() {
return "信息为 name=" + name + " age=" + age + " 薪水=" + salary;
}
}
package com.hspedu;
public class Encapsulation01 {
public static void main(String[] args) {
Person person = new Person();
person.setName("jack8080");
person.setAge(300);
person.setSalary(30000);
System.out.println(person.info());
//使用构造器指定属性
Person smith = new Person("smith", 80, 50000);
System.out.println("===smith的信息===");
System.out.println(smith.info());
}
}
/*
* 要求:年龄,工资为隐私
* 数据验证:年龄(1-120) 不合理给默认年龄
* name 2-6个字符
* */
class Person {
public String name;
private int age;
private double salary;
public Person(){
}
public Person(String name, int age, double salary) {
//用构造器会被破解,要在构造器中使用方法
// this.name = name;
// this.age = age;
// this.salary = salary;
setName(name);
setAge(age);
setSalary(salary);
}
//使用快捷键
public String getName() {
return name;
}
public void setName(String name) {
if(name.length() >= 2 && name.length() <= 6){
this.name = name;
}else {
System.out.println("name 需要在2-6个字符");
this.name = "无名人";
}
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age >= 1 && age <= 120){
this.age = age;
} else {
System.out.println("输入年龄需要在 1-120");
this.age = 18; //给默认年龄
}
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
//写一个方法,返回属性信息
public String info() {
return "信息为 name=" + name + " age=" + age + " 薪水=" + salary;
}
}
1.代码复用,在父类中定义相同的属性和方法
2.拓展性,维护性提高了
1.可以用父类的公共方法将私有属性,方法返回数据给子类
访问级别 | 访问控制修饰符 | 同类 | 同包 | 子类 | 不同包 |
公开 | public | √ | √ | √ | √ |
受保护 | protected | √ | √ | √ | × |
默认 | 没有修饰符 | √ | √ | × | × |
私有 | private | √ | × | × | × |
2.子类必须调用父类的构造器,完成父类初始划 默认子类有super();调用父类无参构造器。若父类没有无参构造器,得用super(参数列表)去指定。
3.子类调用父类的构造器,要将super放在第一行
4.super()和this()都只能放在构造器第一行,因此两个方法不能共存在一个构造器
5.Object是所有类的基类 ctrl + H 可以看到类的继承关系(IDEA)
6.子类最多只能继承一个父类,且与父类要有逻辑关系
public class ExtendsExercise {
public static void main(String[] args) {
B b = new B();
}
}
class A {
A() {
System.out.println("a");
}
A(String name) {
System.out.println("a name");
}
}
class B extends A {
B(){
this("abc");
System.out.println("b");
}
B(String name) {
System.out.println("b name");
}
}
结果:
方法重写是指在子类中重新定义(或覆盖)其父类中已有的方法,使其具有不同的实现。方法重写是面向对象编程中实现多态性的重要手段之一。
方法重写需要满足以下条件:
方法重写使得子类可以根据自己的需要实现特定的行为,增强了代码的可读性和可维护性,同时提高了代码的灵活性和扩展性。
是面向对象编程中的两个重要概念,它们都可以实现多态性,但它们之间存在明显的区别。下面是一个表格,总结了方法重载和方法重写的主要区别:
特征 | 方法重载 | 方法重写 |
---|---|---|
定义 | 在同一类中定义多个同名方法,参数列表不同 | 在子类中重新定义父类中的方法 |
目的 | 实现多态性,提供不同的实现方式 | 实现多态性,子类根据需要实现特定行为 |
参数列表 | 不同方法名,参数列表不同 | 相同方法名,参数列表相同 |
返回类型 | 可以不同,但通常是相同或子类关系 | 相同或子类关系 |
访问修饰符 | 可以不同,但通常是相同或更宽松的限制 | 可以不同,但通常是相同或更宽松的限制 |
异常类型 | 可以不同,但通常是相同或更宽松的限制 | 相同或子类关系 |
执行时间 | 编译时多态性,在编译时确定实际调用的方法 | 运行时多态性,在运行时确定实际调用的方法 |
使用场景 | 方法重载通常用于操作类别的不同对象 | 方法重写通常用于子类扩展父类的行为或接口的实现 |
这个表格总结了方法重载和方法重写的主要区别。通过理解这些区别,开发人员可以更好地运用这两个概念来设计和实现面向对象的程序。
着重注意super调用构造器和方法
package com.hspedu.extend_.exercise;
public class OverrideExercise {
public static void main(String[] args) {
Person jack = new Person("jack", 10);
System.out.println(jack.say());
student smith = new student("smith", 20, 123456, 99.8);
System.out.println(smith.say());
}
}
package com.hspedu.extend_.exercise;
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String say() {
return "name=" + name + " age=" + age;
}
// 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;
// }
}
package com.hspedu.extend_.exercise;
public class student extends Person {
private int id;
private double score;
public student(String name, int age, int id, double score) {
super(name, age);
this.id = id;
this.score = score;
}
public String say() {
return super.say() + " id=" + id + " score=" + score;
}
// public int getId() {
// return id;
// }
//
// public void setId(int id) {
// this.id = id;
// }
//
// public double getScore() {
// return score;
// }
//
// public void setScore(double score) {
// this.score = score;
// }
}
结果:
多态是建立在封装和继承的基础上的。两个对象(类)要存在继承关系
重写和重载就体现多态。
(1)一个对象的编译类型和运行类型可以不一致
(2) 编译类型在定义对象时,就确定了,不能改变
(3)运行类型是可以变化的.
(4)编译类型看定义时=号 的左边,运行类型看=号的右边
eg:父类的引用指向子类的对象
Animal animal = new Dog(); [animal编译类型是Anima,运行类型是Dog]
animal = new Cat();[animal 运行类型变为Cat, 编译类型仍为Animal]
ps:找的是运行类型的方法 ,在编译阶段,调用由编译类型决定
1) 本质:父类的引用指向了子类的对象 Animal animal = new Dog(); //运行类型为编译类型的子类
2) 语法:父类类型引用名 = new 子类类型();
3) 特点:编译类型看左边,运行类型看右边。
可以调用父类中的所有成员(需遵守访问权限),不能调用子类中特有成员,下方不能写animal.catchMouse;预知如何解局,请看向下转型。
最终运行效果看子类的具体实现!
package com.hspedu.extend_.exercise;
public class poly {
public static void main(String[] args) {
//多态的向上本质:父类的引用指向了子类的对象
//语法: 父类类型引用名 = new 子类类型()//object obj = new Cat();
Animal animal = new Cat();
System.out.println("ok");
//最终效果看子类的具体实现
animal.eat();
animal.run();
animal.show();
animal.sleep();
}
}
//-------------------------------
package com.hspedu.extend_.exercise;
public class Animal {
String name = "动物";
int age = 10;
public void sleep(){
System.out.println("睡");
}
public void run(){
System.out.println("跑");
}
public void eat(){
System.out.println("吃");
}
public void show(){
System.out.println("hello,你好");
}
}
//----------------------------------
package com.hspedu.extend_.exercise;
public class Cat extends Animal{
public void eat(){ //方法重写
System.out.println("猫吃鱼");
}
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
1) 语法:子类类型引用名=(子类类型)父类引用;
2)只能强转父类的引用,不能强转父类的对象
3)要求父类的引用必须指向的是当前目标类型的对象
4)当向下转型后,可以调用子类类型中所有的成员
//希望可写cat.catchMouse;
//向下转型
//语法:子类类型引用名=(子类类型)父类引用;
Cat cat = (Cat) animal;//此时,编译类型和运行类型皆为Cat
cat.catchMouse();
1.属性没有重写之说!属性的值看编译类型 //方法从运行类型开始查找
package com.hspedu.extend_.exercise;
public class PolyDetail {
public static void main(String[] args) {
Base base = new Sub();
System.out.println(base.count);
}
}
class Base {
int count = 10;
}
class Sub extends Base {
int count = 20;
}
结果为 :10
2.在Java中,instanceof
是一个关键字,用于判断一个对象是否属于某个类(子类)或接口。返回值为boolean类型。
eg:System.out.println(base instanceof Base);反映对象(运行类型)与类的关系。
1. 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定 //运行类型有的方法优先使用运行类型。
2. 当调用对象属性时,没有动态绑定机制,哪里声明,那里使用
package com.hspedu.extend_.exercise.polyarr;
public class PloyArray {
public static void main(String[] args) {
Person[] persons = new Person[5];
persons[0] = new Person("jack", 20);
persons[1] = new Student("jack", 18, 100);
persons[2] = new Student("smith", 19, 30.1);
persons[3] = new Teacher("scott", 30, 20000);
persons[4] = new Teacher("king", 50, 25000);
//循环遍历多态数组,调用say
for (int i = 0; i < persons.length; i++) {
//person[i] 编译类型是 Person,运行类型根据实际情况由JVM来判定
System.out.println(persons[i].say());//动态绑定机制
}
}
}
------------------分割线-------------------
package com.hspedu.extend_.exercise.polyarr;
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String say() {
return name + "\t" + age;
}
}
------------------分割线-------------------
package com.hspedu.extend_.exercise.polyarr;
public class Student extends Person{
private double score;
public Student(String name, int age, double score) {
super(name, age);
this.score = score;
}
//重写父类的say方法
@Override
public String say() {
return super.say() + " score=" + score;
}
}
------------------分割线-------------------
package com.hspedu.extend_.exercise.polyarr;
public class Teacher extends Person{
private double salary;
public Teacher(String name, int age, double salary) {
super(name, age);
this.salary = salary;
}
@Override
public String say() {
return super.say() + " salary" + salary;
}
}
结果:
综合进阶:
package com.hspedu.extend_.exercise.polyarr;
public class PloyArray {
public static void main(String[] args) {
Person[] persons = new Person[5];
persons[0] = new Person("jack", 20);
persons[1] = new Student("jack", 18, 100);
persons[2] = new Student("smith", 19, 30.1);
persons[3] = new Teacher("scott", 30, 20000);
persons[4] = new Teacher("king", 50, 25000);
//循环遍历多态数组,调用say
for (int i = 0; i < persons.length; i++) {
//person[i] 编译类型是 Person,运行类型根据实际情况由JVM来判定
System.out.println(persons[i].say());//动态绑定机制
//调用特有方法 --> 向下转型
//只能强转父类的引用,不能强转父类的对象
if(persons[i] instanceof Student) {
((Student)persons[i]).study(); //向下转型
} else if (persons[i] instanceof Teacher) {
((Teacher)persons[i]).teach(); //向下转型
} else {
System.out.println("类型有误");
}
}
}
}
------------------分割线-------------------
package com.hspedu.extend_.exercise.polyarr;
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//构造器不能直接使私有属性公有化,但可以通过在构造器中设置公共方法来间接实现。
public String getName() {
return name;
}
// public void setName(String name) {
// this.name = name;
// }
public String say() {
return name + "\t" + age;
}
}
------------------分割线-------------------
package com.hspedu.extend_.exercise.polyarr;
public class Student extends Person{
private double score;
public Student(String name, int age, double score) {
super(name, age);
this.score = score;
}
//重写父类的say方法
@Override
public String say() {
return super.say() + " score=" + score;
}
//特有方法
public void study() {
System.out.println("学生 " + getName() + " 在学java");
}
}
------------------分割线-------------------
package com.hspedu.extend_.exercise.polyarr;
public class Teacher extends Person{
private double salary;
public Teacher(String name, int age, double salary) {
super(name, age);
this.salary = salary;
}
@Override
public String say() {
return super.say() + " salary" + salary;
}
//特有方法
public void teach() {
System.out.println("老师 " + getName() + " 在讲课");
}
}
结果:
jack 20
类型有误
jack 18 score=100.0
学生 jack 在学java
smith 19 score=30.1
学生 smith 在学java
scott 30 salary20000.0
老师 scott 在讲课
king 50 salary25000.0
老师 king 在讲课
Process finished with exit code 0
形参类型为父类类型,实参类型允许为子类类型