一、封装
1)、概念:封装是把过程和数据包围起来,数据被保护在内部,对数据的访问只能通过已定义的接口,即只能通过已定义的方法去访问这些私有属性
2)、封装的好处:能够实现对数据保护性,封装就是隐藏了类或方法的具体实现细节降低了软件的耦合度,便于软件的更新与改动,将类中的私有属性封装后,这样更安全,封装后的调用代码重用性高,安全。
3)、实现封装步骤:
- 改属性的私有性来限制对属性的访问(一般限制为private)
- 对每个值的属性提供对外的公共方法访问,也就是创建一对赋取值方法,用于对私有属性的访问与赋值,可以对输入的数据进行验证。
4)、实例:
public class Account { //定义三个private属性,私有属性的范围只能由该类中的函数、其友元函数访问,不能被任何其他访问,该类 //的对象也不能访问 private String name; private double balance; private String password; public Account() { //定义一个无参构造器 } public Account(String name, double balance, String password) { //定义一个有参构造器调用set方法对三个属性进行设置 setBalance(balance); setName(name); setPassword(password); } //定义获取name属性的公开方法 public String getName() { return name; } //定义设置name属性的公开方法,可以通过方法对name属性进行验证,提高安全性 public void setName(String name) { if (name.length()>=2 && name.length()<=4){ this.name = name; }else{ System.out.println("姓名输入有误!"); } } //定义获取余额属性的公开方法 public double getBalance() { return balance; } //定义设置balance属性的公开方法,可以通过方法对balance属性进行验证 public void setBalance(double balance) { if (balance >= 20.0) { this.balance = balance; }else{ System.out.println("余额必须大于20块钱!"); } } //定义获取密码属性的公开方法 public String getPassword() { return password; //这里可以进行自定义验证 } //定义设置password属性的公开方法 public void setPassword(String password) { if (password.length() == 6){ this.password = password; }else{ System.out.println("密码必须是6位数!"); this.password="123456"; System.out.println("默认密码为123456!"); } } public void print(){ System.out.println("用户:"+name+" "+"余额是:"+balance); } }
主方法调用:
public class AccountTest { public static void main(String[] args) { //调用有参构造器,传入参数,会在构造器里面调用方法,进行数据验证 Account account = new Account("mike",2000,"1234256"); //print方法用来输出想要获取的数据,这里也可以单独调用get方法获取个别属性 account.print(); } }
采用 this 关键字是为了解决实例变量(private String name)和局部变量(setName(String name)中的name变量)之间发生的同名的冲突。
总结:封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段,适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。
二、继承
1)、继承的概念:封装是把过程和数据包围起来,数据被保护在内部,对数据的访问只能通过已定义的接口,即只能通过已定义的方法去访问这些私有属性。
例子:🐇和🐏属于食草动物类,🦁和🐅属于食肉动物类。
食草动物和食肉动物又是属于动物类。
所以继承需要符合的关系是:is-a,父类更通用,子类更具体。
虽然食草动物和食肉动物都是属于动物,但是两者的属性和行为上有差别,所以子类会具有父类的一般特性也会具有自身的特性。
2)、继承的好处:“继承就是子类继承父类,使子类具有父类的各种属性和方法,好处就是避免多余的代码出现 , 代码的复用性提高了 , 代码的扩展性和维护性提高了,为多态提供了前提。
3)、继承的基本语法:class 子类 extends 父类{ }
子类会继承父类的方法和属性
子类也可以定义特定的方法和属性
注意!!!
(1) 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访 问,要通过父类提供公共的方法去访问
(2) 子类必须调用父类的构造器, 完成父类的初始化
(3) 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无 参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译出错!
(4) 如果希望指定去调用父类的某个构造器,则显式的调用一下 : super(参数列表)
(5) super 在使用时,必须放在构造器第一行(super 只能在构造器中使用)
(6) super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
(7) java 所有类都是 Object 类的子类, Object 是所有类的基类.
(8) 父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类(顶级父类)
(9) 子类最多只能继承一个父类(指直接继承),即 java 中是单继承机制。 若是要何让 A 类继承 B 类和 C 类可以使用多重继承【A 继承 B, B 继承 C】
(10) 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系
4)、案例演示:
定义一个Student类
public class Student { public String name; public int age; private double score; //定义一个私有属性 public void setScore(double score) { //定义一个设置属性的方法 this.score = score; } public void showInfo(){ //输出信息 System.out.println("学生:"+name+"年龄:"+age+"成绩:"+score); } }
定义一个Graduate类继承Student类
public class Graduate extends Student { public void testing(){ System.out.println("大学生"+name); } }
定义一个Pupil类继承Student类
public class Pupil extends Student { public void testing(){ System.out.println("小学生"+name); } }
进行测试:
public class Extends01 { //测试类 public static void main(String[] args) { Pupil pupil = new Pupil(); //new一个pupil对象引用 pupil.name="tong"; //可以修改父类的非私有属性 pupil.age=16; pupil.setScore(90); //对于私有属性只能通过公开方法进行调用设置 pupil.showInfo(); System.out.println("==============="); Graduate graduate = new Graduate(); //同理可得 graduate.name="tongtong"; graduate.age=18; graduate.setScore(190); graduate.showInfo(); } }
此时的pupil无法使用父类的私有属性,显示如下:
运行结果为
5)、内存分析:
super 关键字
1)、概念:super可用于访问父类被子类隐藏或着覆盖的方法和属性,使用形式为super.方法(属性),调用构造器为super(参数列表)
2)、super的细节与好处:
(1)、能够调用父类的构造器,使父类的构造器由父类初始化,子类构造器由子类进行初始化
(2)、父类与子类中属性与方法重名时,可以使用super去调用父类的方法属性
(3)、在类的继承中,子类的构造方法中默认会有super()语句存在,若显示时必须放在第一行
(4)、必须遵循访问权限的规则,遵循就近原则
3)、super 和 this 的比较
三、多态
1)、多态概念:父类的引用可以引用子类的对象,多态是同一个行为具有多个不同表现形式或形态的能力。
2)、多态的好处:
- 提高了程序的扩展性
- 降低了代码之间的耦合
提高了代码的维护性
3)、多态存在的条件:
- 有继承
- 有方法重写
- 有父类引用指向子类对象
4)、案例演示:
创建一个Master类
public class Master { private String name; //设置私有属性 public Master(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void feed(Animal a, Food f){ //传入父类类型,进行调用子类的方法,指向子类的对象, //调用方法时从子类开始调用 System.out.println("主人"+name+"给"+a.getName()+"吃"+f.getFood()); } }
feed方法可以被Animal的多个子类进行调用,传入父类引用子类对象就不用一个子类就重载一次feed方法。
Animal类
public class Animal { private String name; public Animal(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
Cat类
public class Cat extends Animal{ public Cat(String name) { super(name); //调用父类的有参构造器 } }
Dog类
public class Dog extends Animal{ public Dog(String name) { super(name); //同理,在主方法调用此构造器时传入的值再传向父类进行父类属性初始化 } }
Food类
public class Food { private String food; public Food(String food) { this.food = food; } public String getFood() { return food; } public void setFood(String food) { this.food = food; } }
Fish类
public class Fish extends Food{ public Fish(String food) { super(food); //调用父类的有参构造器 } }
Bone类
public class Bone extends Food{ public Bone(String food) { super(food); } }
主方法运行
public class poly { public static void main(String[] args) { Master tim = new Master("tim"); Dog dog = new Dog("小文"); Bone bone = new Bone("骨头"); tim.feed(dog,bone); //将cat对象传入后,feed方法会接收其对象 Cat cat = new Cat("小花猫"); Fish fish = new Fish("小黄鱼"); tim.feed(cat,fish); //同理 } }
Master类通过Animal类和Food类使得同一个喂食物行为具有多个不同表现形式或形态的能力
向上转型
1)、概念:父类的引用指向了子类的对象,称为向上转型
2)、实例:语法:父类类型引用名 = new 子类类型(),Father f = new Son(); // 这就叫 upcasting (向上转型)
// 现在 f 引用指向一个Son对象
3)、规则:
向上转型调用方法的规则如下:
(1)可以调用父类中的所有成员(需遵守访问权限)
(2)但是不能调用子类的特有的成员 ,因为在编译阶段,能调用哪些成员,是由编译类型来决定的(在等号左边的类型为编译类型)
(3)最终运行效果看子类(运行类型)的具体实现, 即调用方法时,按照从子类(运行类型)开始查找方法 ,然后调用,规则与方法调用规则一致
注意熟记!!
编译类型看左边,运行类型看右边,调用方法为编译类型决定的,无法调用子类的私有方法,(即没有重写的方法)
例:
public class Animal { String name = "动物"; int age = 10; public void eat(){ System.out.println("吃"); } }
public class Cat extends Animal { public void eat(){//方法重写 System.out.println("猫吃鱼"); } public void catchMouse(){ //Cat 特有方法 System.out.println("猫抓老鼠"); } }}
Animal animal = new Cat(); 此时的animal对象引用只能调用子类重写父类的方法,而无法调用特有方法,称为向上转型
向下转型
1)、概念:与向上转型相反,即是把父类对象转为子类对象
2)、实例:语法:子类类型 引用名 = (子类类型) 父类引用
Father f = new Son(); // 这就叫 upcasting (向上转型)
// 现在 f 引用指向一个Son对象
Son s1 = (Son)f1; // 这就叫 downcasting (向下转型)
// 现在f1 还是指向 Son对象(但可以调用子类的方法)
3)、规则:
向下转型调用方法的规则如下:
(1)只能强转父类的引用,无法强转父类的对象
(2)要求父类的引用必须指向当前目标类型的对象
以上面为例子:
public class Animal { String name = "动物"; int age = 10; public void eat(){ System.out.println("吃"); } }
public class Cat extends Animal { public void eat(){//方法重写 System.out.println("猫吃鱼"); } public void catchMouse(){ //Cat 特有方法 System.out.println("猫抓老鼠"); } }}
Cat cat = new animal(); 此时的编译类型和运行类型同为Cat,故可以调用cat的方法,称为向下转型
注意:属性没有重写之说!属性的值看编译类型
class Base { //父类 int count = 10; //属性 } class Sub extends Base { //子类 int count = 20; //属性 } public static void main(String[] args) { //属性没有重写 Base base = new Sub();//向上转型 System.out.println(base.count);// 看编译类型,编译类型为base,故为base下的属性,为10 Sub sub = new Sub(); System.out.println(sub.count);//?编译类型为sub,故为sub下的属性,为20 }
instanceof 关键字
instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类类型或者时子类型的实例,返回 boolean 的数据类型
public static void main(String[] args) { BB bb = new BB(); System.out.println(bb instanceof BB);// true System.out.println(bb instanceof AA);// true //aa 编译类型 AA, 运行类型是 BB //BB 是 AA 子类 AA aa = new BB(); System.out.println(aa instanceof AA); //故为真 System.out.println(aa instanceof BB); //BB为AA子类,为真 Object obj = new Object(); System.out.println(obj instanceof AA); //false,AA类与Object无关系 String str = "hello"; System.out.println(str instanceof Object);//true } } class AA {} class BB extends AA {}