隐藏对象内部的属性和实现细节,仅对外公开接口来和对象进行交互。
例如对于电脑这样一个复杂的设备,提供给用户的就只是:开关机、通过键盘输入,显示器,USB插孔等,让用户来和计算机进行交互,完成日常事务。但实际上电脑真正工作的却是CPU、显卡、内存等一些硬件元件,而这些元件并不直接对用户开放。
Java中主要通过类和权限修饰符来实现封装,类可以将数据和操作数据的方法结合在一起,更符合人类对事物的认知,权限修饰符用来控制,外部对类内部数据的访问。
权限范围:
权限使用:
【注】当父类和子类不在同一个包内,子类对于父类内被protected修饰的成员,只能通过super进行访问。
抽取对多个类的共同属性和行为到某个类,其他的类进行继承,可以实现对这些属性和行为的复用,无需重复定义。如果子类需要扩展可以重写父类方法,或者添加新的属性和方法。
代码的复用
通用extends关键字进行继承
public class Animal{ protected String name; protected void eat(){ }} /** * Dog继承了Animal的name和eat() */public class Dog extends Animal{ protected String name; protected void eat(){ }}
【注】
调用子类构造方法产生子类对象之前,默认调用父类的构造方法先产生父类对象而后产生子类对象
Java中不允许多重继承,但允许多层继承
Java中一个类只能使用extends继承一个父类,不能同时extends继承多个父类
如下继承语法时错误
public class A{}public class B{}//这种语法时错误的public class C extends A,B{}
super.属性名 访问的时父类的属性
1)当子类有属性和父类同名时,访问的是子类属性,输出的是子类属性值
public class Parent { protected String name = "爸爸";}class Son extends Parent{ protected String name="儿子"; public void printInfo(){ System.out.println(super.name); //输出“爸爸” System.out.println(name); //输出“儿子” } public static void main(String[] args) { Son son = new Son(); son.printInfo(); }}
输出结果如下图:
2)当子类没有属性和父类同名时,访问的是父类的属性,输出的是父类属性值
public class Parent { protected String name = "爸爸";}class Son extends Parent{ //protected String name="儿子"; //注释掉看输出的效果 public void printInfo(){ System.out.println(super.name); //输出“爸爸” System.out.println(name); //也是输出“爸爸” } public static void main(String[] args) { Son son = new Son(); son.printInfo(); }}
输出结果如下图:
1)父类有默认的无参构造器,子类构造器会默认去调用父类的无参构造器
public class Parent { public Parent(){ System.out.println("爸爸1"); } public Parent(String name){ System.out.println(name); }}class Son extends Parent{ public Son(){ System.out.println("儿子"); } public static void main(String[] args) { Son son = new Son(); }}
输出结果如下图:
2)父类没有默认的无参构造器,子类构造器需要手动去调用父类的其他构造器
父类注释默认无参构造器后报错:
父类注释掉默认的无参构造器后,子类构造器内部去调用父类其他构造器,编译通过
super访问普通方法,逻辑同访问属性一致,这里不再赘述,可参考2.4.3.1
1)修饰变量或字段,表示常量(即不能修改)
final int a = 10;a = 20; // 编译出错
2)修饰类表示此类不能被继承
final public class Animal {...}public class Bird extends Animal {...}// 编译出错Error:(3, 27) java: 无法从最终com.bit.Animal进行继承
我们平时使用的 String 字符串类, 就是用 final 修饰的, 不能被继承
3)修饰方法,表示方法不能被重写
当同一种类型的对象,去做同一种行为的时候,所体现的效果不同。
例如打印机有彩色打印机和黑白打印机,它们都有”打印文件“的行为,但是打印的文件颜色效果不同,这就是多态的一种体现。
多态在代码中体现
/** * 打印机 */public abstract class Printer { protected void print(){ System.out.println("打印出未知颜色的文件"); }}/** * 黑白打印机 */class BlackWhitePrinter extends Printer{ @Override protected void print() { System.out.println("打印出黑白的文件"); }}/** * 彩色打印机 */class ColorfulPrinter extends Printer{ @Override protected void print() { System.out.println("打印出彩色的文件"); }}class Runner{ public static void main(String[] args) { Printer pr1 = new BlackWhitePrinter(); Printer pr2 = new ColorfulPrinter(); //pr1和pr2都是用Printer类型的引用进行引用,打印出效果不同,体现了多态。 pr1.print(); pr2.print(); }}
上述代码,基类为Printer,BlackWhitePrinter和ColorfulPrinter都继承了Printer,并重写了Printer的print(),调用的时候,BlackWhitePrinter和ColorfulPrinter的对象都是用Printer类型的变量进行引用,调用了同一个方法,但是产生的效果不同,这是多态的体现。
输出结果如下:
定义
向上转型:实际就是创建一个子类对象,将其当成父类对象来使用。
语法格式:父类类型 对象名 = new 子类类型()
Animal animal = new Cat("元宝",2);
使用场景
public class TestAnimal { // 2. 方法传参:形参为父类型引用,可以接收任意子类的对象 public static void eatFood(Animal a){ a.eat(); } // 3. 作返回值:返回任意子类对象 public static Animal buyAnimal(String var){ if("狗".equals(var) ){ return new Dog("狗狗",1); }else if("猫" .equals(var)){ return new Cat("猫猫", 1); }else{ return null; } } public static void main(String[] args) { Animal cat = new Cat("元宝",2); // 1. 直接赋值:子类对象赋值给父类对象 Dog dog = new Dog("小七", 1); eatFood(cat); eatFood(dog); Animal animal = buyAnimal("狗"); animal.eat(); animal = buyAnimal("猫"); animal.eat(); }}
【注】
将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的 方法,此时:将父类引用再还原为子类对象即可,即向下转换.
public class TestAnimal { public static void main(String[] args) { Cat cat = new Cat(); Dog dog = new Dog(); Animal animal = cat; //向上转型 animal.eat(); //输出“cat eat",当前实际类型为Cat animal = dog; //指向dog对象 animal.eat(); //输出“dog eat",,当前实际类型为Dog // cat = (Cat)animal; //animal类型为Animal,实际类型为Dog,向下转型为Cat,虽然编译通过,但是运行时报错 // cat.mew(); dog = (Dog)animal; //animal引用类型为Animal,实际类型为Dog,向下转型为Dog,编译通过,运行也能通过 dog.bark(); }}class Animal{ protected void eat(){};}class Cat extends Animal{ @Override protected void eat() { System.out.println("cat eat"); } public void mew(){ System.out.println("喵喵"); }}class Dog extends Animal{ @Override protected void eat() { System.out.println("dog eat"); } public void bark(){ System.out.println("汪汪"); }}
向下转型用的比较少,而且不安全,万一转换失败,运行时就会抛异常。Java中为了提高向下转型的安全性,引入 了 instanceof ,如果该表达式为true,则可以安全转换。
public class TestAnimal { public static void main(String[] args) { Cat cat = new Cat(); Dog dog = new Dog(); Animal animal = cat; //向上转型 animal.eat(); //输出“cat eat",引用类型为Animal,当前实际类型为Cat animal = dog; //指向dog对象 animal.eat(); //输出“dog eat",引用类型为Animal,当前实际类型为Dog if (animal instanceof Cat) { //判断实际类型是否为Cat cat = (Cat)animal; cat.mew(); } if (animal instanceof Dog) { //判断实际类型是否为Dog dog = (Dog)animal; dog.bark(); } }}class Animal{ protected void eat(){};}class Cat extends Animal{ @Override protected void eat() { System.out.println("cat eat"); } public void mew(){ System.out.println("喵喵"); }}class Dog extends Animal{ @Override protected void eat() { System.out.println("dog eat"); } public void bark(){ System.out.println("汪汪"); }}