大家来认识下面向对象三大特征
封装、继承、多态
封装
介绍
封装就是把抽象出的数据(属性)和对数据的操作(方法)封装在一起,数据被保护在内部,
程序的其他部分只有通过被授权的操作{方法},才能对数据进行操作
封装的实现步骤
1.将属性进行私有化private【不能直接修改属性】
2.提供一个公共的(public)set方法,用于对属性判断并赋值
public void setVVV(类型 参数名){//VVV表示某个属性
//加入数据验证的业务逻辑
属性= 参数名;
}
3.提供一个公共的get方法,用于获取属性的值
public xx getVVV(){//权限判断
return xx;
}
练习
public class class1 {
/**
* 一个小程序,不能随便查看人的年龄,工资等隐私
* 并对设置的年龄进行合理地验证。年龄合理就设置,否则给默认
* 年龄必须在1-120,年龄,工资不能直接擦好看,name的长度在2-6之间
*/
public static void main(String[] args) {
person per = new person();
per.setName("刘天柱");
per.setAge(18);
per.setSalary(15000);
System.out.println(per.info());
person son = new person("smith", 2000, 50000);
System.out.println(son.info());
}
}
class person{
public String name; //公开名字
private int age; //私有年龄
private double salary; //私有工资
public void setName(String name){
if (name.length() >= 2 && name.length() <= 6)
this.name = name;
else
System.out.println("名字默认长度不对,请重新输入(2-6)gezifu");
}
public person(){
}
public person(String name, int age, double salary) {
setName(name);
setSalary(salary);
setAge(age);
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
//返回属性信息
public String info(){
return "信息为 name="+ name + "年龄=" + age +"薪水="+salary;
}
}
继承
继承机制图
继承的优点
1.代码的复用性提高了
2.代码的扩展性和维护性提高了
练习
public class extendl {
public String name;
public int age;
public double score;
}
public class Pupil extends extendl {
public void setScore(double score) {
this.score = score;
}
public void test(){
System.out.println("小学生"+name+"正在小学数学");
}
public void showInto(){
System.out.println("小学生"+name+"年龄"+age+"成绩"+score);
}
}
class ftu{
public String name;
public int age;
private double score;
public void setScore(double score) {
this.score = score;
}
public void test(){
System.out.println("小学生"+name+"正在大学数学");
}
public void showInto(){
System.out.println("小学生"+name+"年龄"+age+"成绩"+score);
}
}
public class Extend1 {
public static void main(String[] args) {
Pupil publi = new Pupil();
publi.name = "xiao";
publi.age = 10;
publi.test();
publi.setScore(60);
publi.showInto();
ftu h1 = new ftu();
h1.name = "tian";
h1.age = 20;
h1.test();
h1.setScore(100);
h1.showInto();
}
}
说明和细节
1.子类继承了所有的属性和方法,但是私有属性和方法不能在子类直接访问,
要通过公共的方法去访问
2.子类必须调用父类的构造器,完成父类的初始化
3.当创建子类对象时,不管使用子类的构造器,默认情况总会去调用父类的无参构造器,
如果父类没有提供无参构造器,则必须在子类的构造器中使用super去指定使用父类的
哪个构造器完成对父类的初始化工作,否则编译不会通过
4.如果希望指定去调用父类的某个构造器,则显示的调用一下:super(参数列表)
5.super在使用时,必须放在构造器第一行
6.super()和this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
7.java所有类都是Object类的子类
8.父类构造器的调用不限于直接父类!将直接往上追溯到object类(最大)
9.子类最多只能继承一个父类(将直接继承),即Java中是单继承机制。
10.不能滥用继承,子类和父类之间必须满足is-a的逻辑关系
super的使用
介绍
super代表父类的引用,用于访问父类的属性、构造方法/构造器、方法
语法
1.访问父类的属性,但不能访问父类private属性
super.属性名;
2.访问父类的方法,不能访问父类的private方法
super.方法名(参数列表);
3.访问父类的构造器
super(参数列表);只能放在构造器的第一句,只能出现一句!
public class A {
public int n1 = 100;
protected int n2 = 200;
int n3 = 300;
private int n4 = 400;
public void test100(){}
protected void test200(){}
void test300(){}
private void test400(){}
public A(String name,int age){}
}
public class B extends A{
//访问父类的属性,但不能访问父类的private属性
public void h1() {
System.out.println(super.n1+" "+super.n2+" "+super.n3);
}
//访问父类的方法,不能访问父类的private方法 super.方法名(参数列表)
public void h2() {
super.test100();
super.test200();
super.test300();
//不能访问父类私有方法super.test400();
}
//访问父类的构造器():super(参数列表);只能放在构造器第一句
public B(){
super("liu",12);
}
}
super的优点
1.调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类的属性由子类初始化)
2.当子类中和父类的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果
没有重名,使用super、this、直接访问都是一样的效果
3.super的访问不限于直接父类。如果爷爷类和本类有同名的成员,也可以使用super去访问爷爷类的
成员;如果多个基类中都有同名的成员,使用super访问遵循就近原则。A->B->C,
也需要遵守访问权限的相关规则
super和this的区别
区别点 | this | super |
---|---|---|
访问属性 | 访问本类中的属性,如果本类没有此属性则从父类中继续查找 | 访问父类中的属性 |
调用属性 | 访问本类中的方法,如果本类没有此方法则从父类继续查找 | 直接访问父类的方法 |
调用构造器 | 调用本类构造器,必须放在构造器的首行 | 调用父类构造器,必须放在子类构造器的首行 |
特殊 | 表示当前对象 | 子类中访问父类对象 |
方法重写/覆盖(override)
介绍
方法覆盖(重写)是指子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,
然后子类方法就覆盖父类的那个方法
public class Animal {
public void cry(){
System.out.println("动物叫唤");
}
}
public class Dog extends Animal{
@Override
public void cry(){
System.out.println("小狗叫唤");
}
public static void main(String[] args) {
Animal d = new Dog();
d.cry();
}
}
注意事项和使用细节
1.子类的方法的参数,方法名称,要和父类方法的参数,方法名称完全一致。
2.子类方法的返回类型和父类方法返回类型一样,也可以是父类返回类型的子类.
父类中的返回类型中的子类的返回类型可以可以写一样的,或者比父类的返回类型小。
public object getInfo(){} public String getInfo(){}
3.子类方法不能缩小类方法的访问权限
public > protected > 默认 > private
方法重写和重载的区别
名称 | 发生范围 | 方法名 | 形参列表 | 返回类型 | 修饰符 |
---|---|---|---|---|---|
重载(overload) | 本类 | 必须一样 | 类型,个数或者顺序至少有一个不同 | 无要求 | 无要求 |
重写(override) | 父子类 | 必须一样 | 相同 | 子类重写的方法,返回的类型和父类返回的类型一致,或者是其子类 | 子类方法不能缩小父类方法的访问范围 |
多态
基本介绍
方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。多态的前提是:两个对象(类)存在继承关系
多态的具体体现
1.方法的多态
重写和重载就体现多态
2.对象多态
1.一个对象的编译类型和运行类型可以不一致
2.编译类型在定义对象时,就确定了,不能改变
3.运行类型是可以变化的。
4.编译类型看定义时 = 号的左边,运行类型看 = 号的右边
Animal animal = new Dog(); animal编译类型是Animal,运行类型是Dog
animal = new Cat(); 运行类型变成了Cat,编译类型仍是Animal
public class Animal {
public void cry(){
System.out.println("动物叫唤");
}
}
public class Dog extends Animal{
@Override
public void cry(){
super.cry();
}
public static void main(String[] args) {
Animal d = new Dog();
d.cry();
}
}
向上转型
1.本质:父类的引用指向了子类的对象。
2.语法:父类类型 引用名 = new 子类类型();
3.特点:编译类型看左边,运行类型看右边。
4.向上转型调用方法的规则
(1).可以调用父类中的所有成员(需遵守访问权限)
(2)不能调用子类中特有成员;
(3)因为在编译阶段,能调用哪些成员,由编译类型决定
(4)最终运行效果看子类的具体实现,即调用方法时,按照从子类(运行类型)
开始查找方法,然后调用,规则我前面我们讲的方法调用规则一致
向下转型
1.语法:子类类型 引用名 = (子类类型) 父类引用
Animal animal = new animal();
Cat cat = (Cat)animal;
2.只能强转父类的引用,不能强转父类的对象
3.要求父类的引用必须指向的是当前目标类型的对象
4.可以调用子类类型中所有成员
属性重写问题
属性没有重写一说!属性的值看编译类型
instanceOF 比较操作符,用于判断对象的运行类型是否为XX类型或XX
类型的子类型
public class Polybn {
public static void main(String[] args) {
BB bb = new BB();
System.out.println(bb instanceof BB);
System.out.println(bb instanceof AA);
AA aa = new BB();
System.out.println(aa instanceof BB);
System.out.println(aa instanceof AA);
Object object = new Object();
System.out.println( object instanceof AA);//false
String str = "荣耀";
System.out.println(str instanceof Object);
}
}
class AA{}
class BB extends AA{}
动态绑定机制
public class A {
public int i =10;
public int sum(){
return getI() +10;
}
public int sum1(){
return i +10;
}
public int getI(){
return i;
}
}
class B extends A{
public int i = 20;
public int sum(){
return i+20;
}
public int getI(){
return i;
}
public int sum1(){
return i+10;
}
}
public class Test {
public static void main(String[] args) {
A a = new B();//向上转型
System.out.println(a.sum());
System.out.println(a.sum1());
}
}
1.在调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
2.在调用对象属性的时候,没有动态绑定机制,哪块声明,就是哪块使用。