day03 【多态、内部类、权限修饰符、代码块】
反馈和内容回顾
1.接口中可以写成员变量吗?
不能! 接口只能写常量,由三个修饰符public static final修饰的
2.接口中都可以写哪种方法?
抽象方法[最常见的]
默认方法
静态方法
3.我们学过的普通类,抽象类,接口中哪些有构造方法?哪些没有构造方法?
普通类有构造方法: a.new对象时使用 b.子类的构造方法第一行调用
抽象类有构造方法: b.子类的构造方法第一行调用
接口没有构造方法(因为接口中没有成员变量,也不需要对成员变量初始化)
4.描述一下Java中类和类的关系,类和接口的关系,接口和接口的关系
类和类:单继承
类和接口: 多实现(类实现接口)
接口和接口: 多继承[了解]
5.接口和抽象类的异同有哪些?[面试题]
a.概念:什么是接口,什么是抽象类
b.相同
都是java文件,编写以后都是.class文件
都是引用数据类型
都不能创建对象
都可以含有抽象方法
子类/实现类继承之后都必须重写所有抽象方法,之后才能创建对象
c.不同
关键字不同
接口中没有成员变量,抽象类中可以有成员变量
接口可以多实现,抽象类只能单继承
抽象类有构造,接口无构造
抽象类中有普通方法,接口中不能有普通方法
今天内容介绍
多态【重点重点重点】
内部类【匿名内部类】
权限修饰符【了解】
代码块【理解】
第一章 多态【重点重点重点】
1.1 多态的概述
面向对象四大特征:
封装,继承,多态,抽象
什么是多态:
a.严格的定义: 同一个动作,具有不同的表现形式
b.不是很严格的定义: 同一个对象,具体不同的形态
举个例子:
比如说你
在学校,是一个学生
在公司,是一个员工
在家里,是一个丈夫/妻子
回老家,是一个孝子
比如一只狗
在家里,一只宠物
在动物园,一只观赏的动物
在餐桌上,一种食物
比如H2O(水)
在常温下,液态水
在高温下,水蒸气
在低温下,冰块
1.2 多态的前提[重点]
a.必须有继承关系或者实现关系
b.必须有方法的重写
只有满足了以上两个前提,才有多态
1.3 多态的体现[重点]
语言表达:
父类类型的变量 指向了 子类类型的对象
代码表达:
Fu ff = new Zi();
举例子:
Animal 动物类 Dog 狗类 (假设Dog已经继承Animal,并且重写了某个方法)
Animal an1 = new Dog();
Animal 动物类 Cat 猫类 (假设Cat已经继承Animal,并且重写了某个方法)
Animal an2 = new Cat();
1.4 多态调用方法的特点[重点]
a.多态调用方法时,编译阶段看父类
b.多态调用方法时,运行阶段看子类
总结: 多态调用方法的特点是编译看父,运行看子(编译看左,运行看右)
/**
* 父类动物
*/
public class Animal {
public void eat(){
System.out.println("动物的吃...");
}
public void sleep(){
System.out.println("动物的睡...");
}
}
/**
* 子类狗类
*/
public class Dog extends Animal{
public void eat(){
System.out.println("狗舔着吃...");
}
public void sleep(){
System.out.println("狗趴着睡...");
}
}
public class TestDemo {
public static void main(String[] args) {
//1.创建Dog对象,并且使用多态接收
Animal an = new Dog();
//2.多态调用方法的特点
//很重要!!!!!!!!!
an.eat();
//a.多态调用方法时,编译阶段看父类
//b.多态调用方法时,运行阶段看子类
}
}
1.5 多态的好处[重点重点重点]
案例:
喂狗吃饭,喂猫吃饭,喂老虎吃饭,喂鸡吃饭
public class Animal {
public void eat(){
System.out.println("动物的吃...");
}
}
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗狗舔着吃...");
}
}
public class Cat extends Animal {
public void eat(){
System.out.println("猫吃鱼...");
}
}
public class TestDemo {
public static void main(String[] args) {
//1.喂狗
Dog wangcai = new Dog();
//feedDog(wangcai);
feedAnimal(wangcai);
//2.喂猫
Cat jf = new Cat();
//feedDog(jf); //报错!! 只能喂狗不能为猫
//feedCat(jf);
feedAnimal(jf);
}
//定义一个方法,喂动物吃饭
public static void feedAnimal(Animal an){
//Animal an = new Dog(); 多态!!
//Animal an = new Cat(); 多态!!
System.out.println("宝贝,过来密西了...");
an.eat();
}
//定义一个方法,喂猫吃饭
public static void feedCat(Cat cc){
System.out.println("宝贝,过来密西了...");
cc.eat();
}
//定义一个方法,喂狗吃饭
public static void feedDog(Dog dd){
System.out.println("宝贝,过来密西了...");
dd.eat();
}
}
总结:
多态的好处是什么???
多态提高了代码的扩展性/灵活性
1.6 多态的弊端
多态调用方法时有一个特点: 编译看父,运行看子
多态的弊端: 使用多态时只能调用子父类都有的那个方法,不能调用子类独有的方法
public class Animal {
public void eat(){
System.out.println("动物吃...");
}
}
public class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗狗吃...");
}
//特有方法
public void lookHome(){
System.out.println("汪汪,滚!!");
}
}
public class TestDemo {
public static void main(String[] args) {
//1.多态的弊端
Animal an = new Dog();
an.eat(); //可以的 ,eat方法子父类都有
an.lookHome(); //报错!!! 因为该方法是子类独有的,父类没有,编译不能通过
}
}
1.7 多态弊端的解决方案-向下转型
基础班:
自动类型转换(从小到大):
double d = 1;(编译器会把int类型转成double类型)
强制类型转换(从大到小)
int i = (int)3.14;
就业班:
向上转型(把子类类型转成父类类型):
Animal an = new Dog(); 多态!!
向下转型(父类类型转回子类类型):
必须有向上转型 才能有向下转型
Dog d = (Dog)an;
我们就可以使用向下转型,解决多态的弊端
public class TestDemo {
public static void main(String[] args) {
//1.多态的弊端
Animal an = new Dog();
an.eat(); //可以的 ,eat方法子父类都有
//an.lookHome(); //报错!!! 因为该方法是子类独有的,父类没有,编译不能通过
//2.向下转型
Dog dd = (Dog)an;
dd.lookHome();
}
}
1.8 转型可能出现的异常
ClassCastException 类型转换异常
什么时候出现这个异常:
当多态是一个子类A,向下转型时是另外一个子类B时,会出现类型转换异常
public class Animal {
public void eat(){
System.out.println("动物吃...");
}
}
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼...");
}
//特有方法
public void catchMouse() {
System.out.println("老鼠,有种别跑...");
}
}
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗狗吃...");
}
//特有方法
public void lookHome(){
System.out.println("汪汪,滚!!");
}
}
public class TestDemo {
public static void main(String[] args) {
//1.创建一个对象,使用多态
// Animal an = new Dog();
Animal an = new Cat();
//2.调用方法
an.eat(); //可以调用
//ClassCastException 类型转换异常
//Cat cannot be cast to Dog
Dog dd = (Dog)an; //必须先向下转型
dd.lookHome();//然后才能调用特有方法
}
}
多态转型时内存情况
1.9 instanceof关键字的介绍
instanceof关键字的作用:
可以判断一个对象,是否是我们指定类的对象
格式:
boolean b = 对象名 instanceof 类名;
public class TestDemo {
public static void main(String[] args) {
//1.创建一个对象,使用多态
Animal an = new Dog();
// Animal an = new Cat();
//2.调用方法
an.eat(); //可以调用
//3.instanceof关键字
if (an instanceof Dog) {
//转成Dog
Dog dd = (Dog)an;
dd.lookHome();
}
if (an instanceof Cat){
//转成Cat
Cat cc = (Cat)an;
cc.catchMouse();
}
}
}
第二章 内部类
2.1 什么是内部类
所谓的内部类就是在一个类A内部定义另外一个类B,此时类B内部类,类A外部类
2.2 成员内部类以及其特点
/**
* 人类,外部类
*/
public class Person {
int age;
String name;
//内部类
//a.成员内部类(心脏类)
class Heart{
int jumpCount; //心跳次数
public void jump(){
System.out.println("心脏砰砰跳~~");
//在成员内部类中可以无条件访问外部类的任何成员
System.out.println(age);
System.out.println(name);
show();
}
}
public void show(){
//b.局部内部类(了解)
class xxx{
}
}
}
成员内部类有两个点:
a.在成员内部类中可以无条件访问外部类的任何成员
b.在测试类中创建成员内部类对象,该怎么写呢???
public class TestDemo {
public static void main(String[] args) {
//1.创建外部类对象
Person p1 = new Person();
//2.创建内部类对象
//Java规定,想要创建内部类的对象,必须先创建外部类的对象
//然后通过外部类对象才能创建内部类对象
//格式:
// Person.Heart 对象名 = new Person().new Heart();
Person.Heart h = new Person().new Heart();
//可以写这么写
Person.Heart h1 = p1.new Heart();
}
}
2.3 内部类编译之后的字节码文件
内部类编译之后的字节码文件名:
外部类名$内部类名.class
比如我们案例中的Heart类,编译之后:
Person$Heart.class
2.4 匿名内部类[重点]
-
什么是匿名内部类
匿名内部类是内部类的简化形式,简化到不需要内部类的名字
-
匿名内部类的作用
匿名内部类可以帮助我们快速的创建一个父类的子类对象或者一个接口的实现类对象!!!
-
匿名内部类的使用1【重点】
//a.定义一个抽象类 public abstract class Animal { public abstract void eat(); public abstract void sleep(); } //b.定义一个子类 继承 抽象类 public class Dog extends Animal { // c.子类重写抽象方法 @Override public void eat() { System.out.println("狗舔着吃..."); } @Override public void sleep() { System.out.println("狗趴着睡..."); } } /** * 需求: 现有一个抽象类,要求我们创建其子类对象,并测试该子类 * a.定义一个抽象类 * b.定义一个子类 继承 抽象类 * c.子类重写抽象方法 * d.创建子类对象 * e.调用子类对象的方法 * */ public class TestDemo { public static void main(String[] args) { //d.创建子类对象 Dog dd = new Dog(); //e.调用子类对象的方法 dd.eat(); dd.sleep(); //使用匿名内部类 Animal an = new Animal(){ @Override public void eat() { System.out.println("狗舔着吃..."); } @Override public void sleep() { System.out.println("狗趴着睡..."); } }; an.eat(); an.sleep(); } }
-
匿名内部类的使用2【重点】
//a.定义接口 public interface Flyable { public abstract void fly(); } //b.创建实现类 实现 接口 public class Bird implements Flyable{ //c.重写接口的所有抽象方法 @Override public void fly() { System.out.println("我想要飞,却飞不高..."); } } /** * 需求: 定义接口,要求创建一个该接口的实现类对象,测试该对象 * a.定义接口 * b.创建实现类 实现 接口 * c.重写接口的所有抽象方法 * d.测试类中创建实现类对象 * e.调用实现类的对象方法 */ public class TestDemo { public static void main(String[] args) { //d.测试类中创建实现类对象 Bird b = new Bird(); //e.调用实现类的对象方法 b.fly(); //匿名内部类 Flyable ff = new Flyable(){ @Override public void fly() { System.out.println("我想要飞,却飞不高..."); } }; ff.fly(); } } 总结匿名内部类的格式: 父类名/接口名 对象名 = new 父类名/接口名(){ //重写父类或者接口中所有抽象方法 };
第三章 权限修饰符
3.1 权限修饰符的介绍
Java中一共有四种常见的权限修饰符:
public protected 不写(默认/default) private
public class Student {
public int age;
protected int age;
int age;
private int age;
}
3.2 不同权限修饰符的访问能力
我们在开发基本上只会用到public和private
一般来说:
成员方法和构造方法是public修饰符的
成员变量是private修饰的
在极个别的情况下,构造方法也可能是private(单例设计模式)
第四章 代码块
什么叫做代码块:
由一对大括号括起来的一句或者多句代码,称为一个代码块
4.1 构造代码块
构造方法代码块的格式:
public class 类名{
{
构造代码块的代码
}
}
public class Dog {
int age;
String name;
public void bark(){
System.out.println("汪汪...");
}
//构造代码块
{
System.out.println("我是构造代码块~~");
System.out.println("我是构造代码块~~");
System.out.println("我是构造代码块~~");
}
}
构造代码块什么时候执行呢???
"每次"调用构造方法创建对象"之前"执行!!
public class TestDemo {
public static void main(String[] args) {
//1.创建Dog对象
Dog d1 = new Dog();
//2.构造代码块是在执行构造方法之前还是之后执行??
//之前
//3.每次构造方法执行时都会执行?还是第一次构造方法执行时才执行呢?
Dog d2 = new Dog();
//每次
}
}
控制台输出:
我是构造代码块~~
我是构造代码块~~
我是构造代码块~~
我是构造方法..
我是构造代码块~~
我是构造代码块~~
我是构造代码块~~
我是构造方法..
4.2 静态代码块
静态代码块的格式:
public class 类名{
static{
静态代码块
}
}
public class Pig {
int age;
String name;
public void sleep(){
System.out.println("呼噜呼噜...");
}
//静态代码块
static {
System.out.println("静态代码块...");
}
}
静态代码块什么时候执行呢???
随着类的加载而执行且执行一次,优先构造方法的执行!!!
总结
能够说出多态的前提
a.必须有继承关系或者实现关系
b.必须有方法的重写
能够写出多态的格式【重点】
格式:
父类类型 对象名 = new 子类类型();
接口类型 对象名 = new 实现类类型();
特点:
多态调方法,编译看父,运行看子
"好处":
提高代码的扩展性【喂猫喂狗案例】
弊端:
多态只能调用子父类共有的方法,无法调用子类特有的方法
能够理解多态向上转型和向下转型
向上转型: 把子类类型 赋值给 父类类型
Animal an = new Dog();
向下转型: 必须有向上转型,才能向下转型
Dog dd = (Dog)an;
关键字:instanceof
if(an instanceof Dog){
Dog dd = (Dog)an;
}
能够说出内部类概念
在一个类的内部定义另外一个类
"能够理解匿名内部类的编写格式"
父类/接口 对象名 = new 父类/接口(){
//重写父类或者接口中的所有抽象方法
};
能够说出每种权限修饰符的作用
public protected 空的 private
代码块
构造代码块: 格式,执行的特点
静态代码块: 格式,执行的特点