在Animal Dog Cat Bird代码中再次理解继承,代码如下:
public class Animal {
String gender ;
int age ;
//父类创建构造函数供子类调用
public Animal(String gender, int age) {
super();
this.gender = gender;
this.age = age;
}
public Animal() {
super();
}
public void sleep() {
System.out.println("就地睡眠");
}
}
//子类Cat
public class Cat extends Animal {
String name;
//如果父类没有构造函数则子类就要一个一个this出来,因此子类调用父类构造函数就是为了节省代码量
/*
* public Cat(String gender,int age,String name) { this.name = name ;
* this.gender =gender; this .age = age; }
*/
public Cat(String gender, int age, String name) {
super(gender, age);
this.name = name;
}
public Cat() {
super();
}
public void eat() {
System.out.println("吃猫粮");
super.sleep();
}
public void sleep() {
System.out.println("在沙发上睡");
}
}
//子类Dog
public class Dog extends Animal {
String name ;
public Dog(String gender, int age, String name) {
super(gender, age);
this.name = name;
}
public Dog() {
super();
}
public void eat() {
System.out.println("吃狗粮");
//用super调用父类睡眠方法
super.sleep();
//如果子类中已经有了睡觉的方法则此时的sleep按照就近原则省略this调用的是子类的方法
//sleep();
}
public void sleep() {
System.out.println("在狗窝睡");
}
}
//子类Bird
public class Bird extends Animal{
//此时Bird的构造函数不能用快捷方式创建因此需要手打,否则无法创建该类对象
public Bird () {
super();
}
public Bird(String name,int age) {
super(name, age);
}
public void eat() {
System.out.println("吃虫子");
}
public void sleep() {
System.out.println("在树上睡");
}
}
总结:1)创建父类继承关系的本质是简化子类的代码量,体现在两个方面
1.1)父类创建构造函数在子类中继承以后可以使子类省去this.name= name之类的代码
1.2)在父类中所写的方法可以通过super.方法名()调用得以实现
2)在理解子类拥有父类答所有属性和方法上,子类对象直接调用即可,若要调用父类的方法和属性要用super调用调用场景(调用属性体现在super.name+"再吃东西",而调用方法则体现在super.eat()此时子类将实现两种方法)
public void eat() {
System.out.println("吃狗粮");
//用super调用父类睡眠方法
super.sleep();
//如果子类中已经有了睡觉的方法则此时的sleep按照就近原则省略this调用的是子类的方法
//sleep();
}
public void sleep() {
System.out.println("在狗窝睡");
}
3)按照就近原则,若子类重写父类方法,在其他方法中调用父类方法时不加super则默认为this.sleep)
4)重写:在继承关系中 子类访问权限修饰符不可减少 返回值类型相同 方法名相同 形参列表相同 方法体不同,且在子类中只要方法名相同系统就会默认你重写了方法,如果不按重写的规则来系统就会报错
5)重载:在一个类中方法名相同 形参列表必须不同 如果形参列表相同就会报错
6)若要继承不同包下的父类需要导包(如果两个相同名字的父类在两个不同的包中,在打出首字母后会提有两个不同的包,此时如果选择不是本类的父类则需要导包)
7)在有些子类中按快捷键不能创建构造函数时需要手动创建构造函数形式和子类继承父类时的构造函数一模一样(如在此案列中Bird无法自动创建构造函数)
封装
1)封装体现在访问权限修饰符和作用域上,可以指定哪些可以访问哪些不可以访问以及在哪里可以用在哪里不可用,体现封装含义.
2)java中处处体现着封装 如int a = 10 ; String b = "hello"; 这两个代码第一个直接将值封装起来而第二个将地址封装起来
3)一个标准类的构成
3.1私有化属性
3.2创建构造函数一个有参一个无参用public修饰
3.3给所有属性提供get和set方法
3.3.1一般get和set使用于被private修饰的成员变量,因为private修饰的成员变量只能在当前类中使用,若要在其他类中使用则要用到,主要起到保护数据的作用因为在main方法中不能直接调用,只能通过set和get方法进行改值.
3.3.2如果在set中输入不符合取值范围的数是可以在set方法里进行if判断
Perseon p = new Perseon();
p.setAge(25);
System.out.println(p.getAge());
3.4重写tostring方法
多态
多态在事儿(方法)上的体现:重写和重载,代码展示如下:
//父类的睡
public class Animal {
public void sleep() {
System.out.println("动物睡觉");
}
//子类重写父类睡方法
public void sleep() {
System.out.println("人在床上睡");
super .sleep();
}
//重载
public void sleep(String name) {
System.out.println(name +"在床上睡");
}
1)注意在子类方法中调用父类的方法只是实现了对象调用一种方法实现多种功能的用途.
2)而多态就体现在睡的多种形式一个是重写 一个是重载
物的多态(子类转父类自动转换 父类转子类强制转换)
Person p = new Person("张三");
Animal animal = p ;
1)即在同一片内存中new出来的东西即可以说他是个人又可以说他是个动物,即物的多态.看本质指的是new出来的对象
Person p = new Person("张三");
//子类转父类自动转换
Animal animal = p ;
//父类转自类强制转换,此时动物是个人
Person p1 = (Person)animal ;
//此时动物在上次转换中已经是人了,因此本质是人,所以人不能转狗因为他们所属两个不同的类
Dog dog = (Dog)animal ;
2)子类转父类自动转无风险 父类转子类强制转换有风险(此处风险指的是动物是条狗此种情况下不能转)因此强转时需要看其本质是啥,本质指new的对象可以使用instenceof进行判断
//用于判断该对象是否属于该类
if(对象名 instenceof 类名){
转换代码;
}
3)子父类相互转化后调方法时确定掉父类方法(此时子父方法名相同)还是子类方法时看本质,本质是谁就调谁的方法,代码如下:
//子类中的方法
public void sleep() {
System.out.println("人在床上睡");
//super .sleep();
}
public void sleep(String name) {
System.out.println(name +"在床上睡");
}
//子转父后调用方法
Person p = new Person("张三");
//子类转父类自动转换
Animal animal = p ;
//父类转自类强制转换
Person p1 = (Person)animal ;
//Dog dog = (Dog)animal ;
animal .sleep();
//结果展示
人在床上睡
如何理解多态使代码更加灵活?(通过人给动物投食案例)
//父类
public class Animal {
public void eat() {
System.out.println("动物吃东西");
}
}
//子类猫
public class Cat extends Animal{
public void catCall() {
System.out.println("猫咪咪叫");
}
}
//子类狗
public class Cat extends Animal{
public void catCall() {
System.out.println("猫咪咪叫");
}
}
//子类人
public class Person {
//没有多态时要为猫等动物必须写三个投食方法并分别传入三种动物!
/*
* public void thro(Cat c) {
*
* } public void thro(Dog d) {
*
* } public void thro(Snake s) {
*
* }
*/
//有多态时代码简化为以下代码:
//此时形参本应为父类对象,但此时却可以传入继承父类的子类对象,因为你此时传子类对象就是将子类自动转化为父类对象了
public void thro(Animal a) {
System.out.println("投食");
//对象调方法,子类继承父类 子类将拥有父类的所有方法和属性,因此直接调
a.eat();
//判断传进来的是不是猫
if (a instanceof Cat ) {
//判断是否为猫,如果是猫为什么要转?父类转子类强转
Cat c= (Cat) a ;
c.catCall();
}else if (a instanceof Dog) {
Dog d = (Dog)a ;
d.dogCall();
}
}
}
//测试类
public class Text {
public static void main(String[] args) {
Person p1 =new Person() ;
Dog dog = new Dog();
Cat cat = new Cat();
Snake s = new Snake();
//传入的动物子类自动转换为父类
p1 .thro(cat);
p1.thro(s);
p1.thro(dog);
}
}
通过人给动物投食理解,总结为下:
1)thro这个方法要给动物投食就要将动物传进来因此形参中数据类型就为动物类而变量名就为动物对象名.但要实现给每只动物都要喂食则要写三次投食方法,因此为了简化代码使代码更加灵活因为继承关系形参为父类,这样当实参中传入子类对象时进入形参中就自动转换为父类,此时的父类需要判断是否为猫类,只有是猫类才能调用自己特有答发方法
2).Animal不管转不转都是父类,只是传入子类对象时自动转换成父类对象了
3)a.eat()实参传进来自动子转父,因此此时父类调自己的方法
4)instance判断此时子类转父类的父类对象是否属于猫类或狗类,如果是则进行强制转换成子类对象,再通过子类调用子类特有的方法.(父转子调子方法)
对物的多态的重新理解(作为对上面物的多态的重新归纳总结)
1何为子转父
即new出来的子类对象,将子类对象转化为父类对象
2何为父转子
有两种转换方式:
A:new出父类的对象然后再将父类对象转为子类对象(该种方式不能转,因为代码段没有报错,但执行结果会报错)即此时父转子有风险,因为此时转不了,代码以及结果展示如下:
//此时系统不会报错,但运行会出错
Father father2 = new Father("张三", 45);
Son s1 = (Son)father2 ;
Exception in thread "main" java.lang.ClassCastException: fatherson.Father cannot be cast to fatherson.Son
at fatherson.Text.main(Text.java:14)
B:先new出子类对象,再将子类对象转换为父类对象,然后再将父类对象转为子类对象,此时就可以转.代码及其运行结果展示如下:
public static void main(String[] args) {
Son son = new Son("张三", 3, "男");
Father father = son ;
Son s1 = (Son)father ;
s1.eat();
//父转子,可以转
s1.sleep();
吃饭
动物睡觉
总结:
1)对于父转子到底能不能转,关键还要看其本质,如在B方法中父类对象的本质是子类对象,因为new出来的是子类对象,所以本质是子类(儿子可以扮老子),在A中new出来的是父类对象,本质是父类因此不能转(老子不能扮儿子).
2)在能转的前提下,子转父只能调用父类的方法,此时不能调用子类的方法.而父转子既可以调用父类方法又可以调子类方法,因位父转子已经是子类对象了,它既有自己的方法同时也继承了父类的方法.