一、复习
1.1 如何定义类
类的定义格式如下:
修饰符 class 类名 {
// 1.成员变量(属性)
// 2.成员方法 (行为)
// 3.构造方法 (初始化类的对象数据的)
}
例如:
public class Student {
// 1.成员变量
public String name ;
public char sex ; // '男' '女'
public int age;
}
1.2 如何通过类创建对象
类名 对象名称 = new 类名();
例如:
Student stu = new Student();
1.3 封装
1.3.1 封装的步骤
1.使用 private
关键字来修饰成员变量。
2.使用public
修饰getter和setter方法。
1.3.2 封装的步骤实现
- private修饰成员变量
public class Student {
private String name;
private int age;
}
- public修饰getter和setter方法
public class Student {
private String name;
private int age;
public void setName(String n) {
name = n;
}
public String getName() {
return name;
}
public void setAge(int a) {
if (a > 0 && a <200) {
age = a;
} else {
System.out.println("年龄非法!");
}
}
public int getAge() {
return age;
}
}
1.4 构造方法
1.4.1 构造方法的作用
在创建对象的时候,给成员变量进行初始化。
初始化即赋值的意思。
1.4.2 构造方法的格式
修饰符 类名(形参列表) {
// 构造体代码,执行代码
}
1.4.3 构造方法的应用
首先定义一个学生类,代码如下:
public class Student {
// 1.成员变量
public String name;
public int age;
// 2.构造方法
public Student() {
System.out.println("无参数构造方法被调用");
}
}
接下来通过调用构造方法得到两个学生对象。
public class CreateStu02 {
public static void main(String[] args) {
// 创建一个学生对象
// 类名 变量名称 = new 类名();
Student s1 = new Student();
// 使用对象访问成员变量,赋值
s1.name = "张三";
s1.age = 20 ;
// 使用对象访问成员变量 输出值
System.out.println(s1.name);
System.out.println(s1.age);
Student s2 = new Student();
// 使用对象访问成员变量 赋值
s2.name = "李四";
s2.age = 18 ;
System.out.println(s2.name);
System.out.println(s2.age);
}
}
1.5 this关键字的作用
1.5.1 this关键字的作用
this代表所在类的当前对象的引用(地址值),即代表当前对象。
1.5.2 this关键字的应用
1.5.2.1 用于普通的gettter与setter方法
this出现在实例方法中,谁调用这个方法(哪个对象调用这个方法),this就代表谁(this就代表哪个对象)。
public class Student {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
if (age > 0 && age < 200) {
this.age = age;
} else {
System.out.println("年龄非法!");
}
}
public int getAge() {
return age;
}
}
1.5.2.2 用于构造方法中
this出现在构造方法中,代表构造方法正在初始化的那个对象。
public class Student {
private String name;
private int age;
// 无参数构造方法
public Student() {}
// 有参数构造方法
public Student(String name,int age) {
this.name = name;
this.age = age;
}
}
第二章 static关键字
2.1 概述
Java中成员(变量和方法)等是存在所属性的,Java是通过static关键字来区分的。**static关键字在Java开发非常的重要,对于理解面向对象非常关键。
关于 static
关键字的使用,它可以用来修饰的成员变量和成员方法,被static修饰的成员是属于类的是放在静态区中,没有static修饰的成员变量和方法则是属于对象的。我们上面案例中的成员变量都是没有static修饰的,所以属于每个对象。
2.2 定义格式和使用
static是静态的意思。 static可以修饰成员变量或者修饰方法。
定义格式
修饰符 static 数据类型 变量名 = 初始值;
举例
public class Student {
public static String schoolName = "传智播客"; // 属于类,只有一份。
// .....
public static void study(){
System.out.println("我们都在黑马程序员学习");
}
}
静态成员变量的访问:
格式:类名.静态变量
格式:类名.静态方法
public static void main(String[] args){
System.out.println(Student.schoolName); // 传智播客
Student.schoolName = "黑马程序员";
System.out.println(Student.schoolName); // 黑马程序员
Student.study();
}
无static修饰的成员方法属于每个对象的,这个成员方法也叫做实例方法。
需要注意的是:实例方法是属于每个对象,必须创建类的对象才可以访问。
格式:对象.实例方法
示例:
public class Student {
// 实例变量
private String name ;
// 2.方法:行为
// 无 static修饰,实例方法。属于每个对象,必须创建对象调用
public void run(){
System.out.println("学生可以跑步");
}
// 无 static修饰,实例方法
public void sleep(){
System.out.println("学生睡觉");
}
public static void study(){
}
}
public static void main(String[] args){
// 创建对象
Student stu = new Student ;
stu.name = "徐干";
// Student.sleep();// 报错,必须用对象访问。
stu.sleep();
stu.run();
}
2.3 注意事项和小结
static的注意事项
- 静态方法只能访问静态变量和静态方法
- 非静态方法可以访问静态变量或者静态方法,也可以访问非静态的成员变量和非静态的成员方法
- 静态方法中是没有this关键字
- 总结:
- 静态方法中,只能访问静态。
- 非静态方法可以访问所有。
- 静态方法中没有this关键字
小结:
1.当 static
修饰成员变量或者成员方法时,该变量称为静态变量,该方法称为静态方法。该类的每个对象都共享同一个类的静态变量和静态方法。任何对象都可以更改该静态变量的值或者访问静态方法。但是不推荐这种方式去访问。因为静态变量或者静态方法直接通过类名访问即可,完全没有必要用对象去访问。
2.无static修饰的成员变量或者成员方法,称为实例变量,实例方法,实例变量和实例方法必须创建类的对象,然后通过对象来访问。
3.static修饰的成员属于类,会存储在静态区,是随着类的加载而加载的,且只加载一次,所以只有一份,节省内存。存储于一块固定的内存区域(静态区),所以,可以直接被类名调用。它优先于对象存在,所以,可以被所有对象共享。
4.无static修饰的成员,是属于对象,对象有多少个,他们就会出现多少份。所以必须由对象调用。
2.4 工具类
不是用来描述一类事物的,而是帮我们做一些事情的类。
- 类名见名知意
- 私有化构造方法
- 方法定义为静态
工具类:
public class ArrayUtil {
//私有化构造方法
//目的:为了不让外界创建他的对象
private ArrayUtil() {
}
//需要定义为静态的,方便调用
public static String printArr(int[] arr) {
StringBuilder sb = new StringBuilder();
sb.append("[");
for (int i = 0; i < arr.length; i++) {
//i 索引 arr[i] 元素
if (i == arr.length - 1) {
sb.append(arr[i]);
} else {
sb.append(arr[i]).append(", ");
}
}
sb.append("]");
return sb.toString();
}
public static double getAverage(double[] arr) {
double sum = 0;
for (int i = 0; i < arr.length; i++) {
sum = sum + arr[i];
}
return sum / arr.length;
}
}
测试类:
public class TestDemo {
public static void main(String[] args) {
//测试工具类中的两个方法是否正确
int[] arr1 = {1, 2, 3, 4, 5};
//直接就是:类名.f方法
String str = ArrayUtil.printArr(arr1);
System.out.println(str);
double[] arr2 = {1.5, 3.7, 4.9, 5.8, 6.6};
double avg = ArrayUtil.getAverage(arr2);
System.out.println(avg);
}
}
第三章 继承
3.1 概述
1.什么是继承、继承的好处?
继承是面向对象三大特征之一,可以让类跟类之间产生子父的关系。
继承**:就是子类继承父类的属性和行为,使得子类对象可以直接具有与父类相同的属性、相同的行为。子类可以直接访问父类中的非私有的属性和行为。
可以把多个子类中重复的代码抽取到父类中,子类可以直接使用,减少代码冗余,提高代码的复用性
提高代码的复用性(减少代码冗余,相同代码重复利用)。使类与类之间产生了关系。
2.继承的格式?
public class 子类 extends 父类{}
3.继承后子类的特点?
子类可以得到父类的属性和行为,子类可以使用。
子类可以在父类的基础上新增其他功能,子类更强大。继承的好处
3.2 继承的特点
- **JAVA只能单继承:**一个类只能继承一个直接父类。
- JAVA不支持多继承、但是支持多层继承。
- JAVA中所有的类都直接或者间接继承于对象类。
- Java中所有的类都直接或者间接继承于Object类。
- 子类只能访问父类中非私有的成员
子类不能继承的内容
- 子类不能继承父类的构造方法。
- 值得注意的是子类可以继承父类的私有成员(成员变量,方法),只是子类无法直接访问而已,可以通过getter/setter方法访问父类的private成员变量。
3.3 继承的格式
通过 extends
关键字,可以声明一个子类继承另外一个父类,定义格式如下:
class 父类 {
...
}
class 子类 extends 父类 {
...
}
需要注意:Java是单继承的,一个类只能继承一个直接父类,跟现实世界很像,但是Java中的子类是更加强大的。
3.4 继承后的特点
3.4.1 成员变量
成员变量不重名:如果子类父类中出现不重名的成员变量,这时的访问是没有影响的。
成员变量重名:如果子类父类中出现重名的成员变量,这时的访问是有影响的。
super访问父类成员变量
1.继承中成员变量访问特点:就近原则。
先在局部位置找,本类成员位置找,父类成员位置找,逐级往上。
2.如果出现了重名的成员变量怎么办?
system.out.println(name); //从局部位置开始往上找
system.out.println(this.name) ; //从本类成员位置开始往上找
system.out.println(super.name); //从父类成员位置开始往上找
public class Test {
public static void main(String[] args) {
Zi z = new Zi();
z.show();
}
}
class Fu {
String name = "Fu";
String hobby = "喝茶";
}
class Zi extends Fu {
String name = "Zi";
String game = "吃鸡";
public void show() {
//如何打印Zi
//System.out.println(name);//Zi
//System.out.println(this.name);//Zi
//如何打印Fu
//System.out.println(super.name);//Fu
//如何打印喝茶
//System.out.println(hobby);//喝茶
//System.out.println(this.hobby);//喝茶
//System.out.println(super.hobby);//喝茶
//如何打印吃鸡
System.out.println(game);
System.out.println(this.game);
}
}
3.4.2 成员方法与方法重写
方法重写 :子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现。
方法重写注意事项和要求:
- 1.重写方法的名称、形参列表必须与父类中的一致。
- 2.子类重写父类方法时,访问权限子类必须大于等于父类(暂时了解︰空着不写< protected<public)
- 3.子类重写父类方法时,返回值类型子类必须小于等于父类
- 4.建议:重写的方法尽量和父类保持一致。
- 5.只有被添加到虚方法表中的方法才能被重写
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0qgK2CFe-1665577746271)(E:\java\Java基础\图片\7\5.png)]
public class Test {
public static void main(String[] args) {
OverseasStudent s = new OverseasStudent();
s.lunch();
}
}
class Person {
public void eat() {
System.out.println("吃米饭,吃菜");
}
public void drink() {
System.out.println("喝开水");
}
}
//留学生
class OverseasStudent extends Person{
public void lunch(){
this.eat();//吃意大利面
this.drink();//喝凉水
super.eat();//吃米饭,吃菜
super.drink();//喝开水
}
//应用场景:当父类中方法,不能满足子类现在的需求时,我们就需要把这个方法进行重写。
//注意:子类中重写的方法上面需要加上@override
@Override
public void eat() {
System.out.println("吃意大利面");
}
@Override
public void drink() {
System.out.println("喝凉水");
}
}
class Student extends Person{
public void lunch(){
//先在本类中查看eat和drink方法,就会调用子类的,如果没有,就会调用从父类中继承下来的eat和drink方法
this.eat();
this.drink();
//直接调用父类中的eat和drink方法
super.eat();
super.drink();
}
}
@Override重写注解
- @Override:注解,重写注解校验!
- 这个注解标记的方法,就说明这个方法必须是重写父类的方法,否则编译阶段报错。
- 建议重写都加上这个注解,一方面可以提高代码的可读性,一方面可以防止重写出错!
3.4.3 构造方法
继承中:构造方法的访问特点
- 父类中的构造方法不会被子类继承。
- 子类中所有的构造方法默认先访问父类中的无参构造,再执行自己。
为什么?
- 子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据。
- 子类初始化之前,一定要调用父类构造方法先完成父类数据空间的初始化。
怎么调用父类构造方法的?
- 子类构造方法的第一行语句默认都是:super(),不写也存在,且必须在第一行。
- 如果想调用父类有参构造,必须手动写super进行调用。
public class Person {
String name;
int age;
public Person() {
System.out.println("父类的无参构造");
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Student extends Person{
public Student(){
//子类构造方法中隐藏的super()去访问父类的无参构造
super();
System.out.println("子类的无参构造");
}
public Student(String name,int age){
super(name,age);
}
}
public class Test {
public static void main(String[] args) {
//创建学生对象
//有参:经过有参构造方法,就不经过
Student s = new Student("zhangsan",23);
//无参:经过无参构造方法
Student s = new Student();
//输出: 父类的无参构造 子类的无参构造
}
}
代码如下:
class Person {
private String name;
private int age;
public Person() {
System.out.println("父类无参");
}
// getter/setter省略
}
class Student extends Person {
private double score;
public Student() {
//super(); // 调用父类无参,默认就存在,可以不写,必须再第一行
System.out.println("子类无参");
}
public Student(double score) {
//super(); // 调用父类无参,默认就存在,可以不写,必须再第一行
this.score = score;
System.out.println("子类有参");
}
}
public class Demo07 {
public static void main(String[] args) {
Student s1 = new Student();
System.out.println("----------");
Student s2 = new Student(99.9);
}
}
输出结果:
父类无参
子类无参
----------
父类无参
子类有参
3.5 super(…)和this(…)
super和this的用法格式
super和this完整的用法如下,其中this,super访问成员我们已经接触过了。
this.成员变量 -- 本类的
super.成员变量 -- 父类的
this.成员方法名() -- 本类的
super.成员方法名() -- 父类的
接下来我们使用调用构造方法格式:
super(...) -- 调用父类的构造方法,根据参数匹配确认
this(...) -- 调用本类的其他构造方法,根据参数匹配确认
小结
- 子类的每个构造方法中均有默认的super(),调用父类的空参构造。手动调用父类构造会覆盖默认的super()。
- super() 和 this() 都必须是在构造方法的第一行,所以不能同时出现。
- super(…)和this(…)是根据参数去确定调用父类哪个构造方法的。
- super(…)可以调用父类构造方法初始化继承自父类的成员变量的数据。
- this(…)可以调用本类中的其他构造方法。
public class Student {
String name;
int age;
String school;
//需求:
//默认为传智大学
public Student() {
//表示调用本类其他构造方法
//细节:虚拟机就不会再添加super();
this(null,0,"传智大学");
}
public Student(String name, int age, String school) {
this.name = name;
this.age = age;
this.school = school;
}
}
public class Test {
public static void main(String[] args) {
Student s = new Student();
}
}
3.6 继承的特点
- Java只支持单继承,不支持多继承。
// 一个类只能有一个父类,不可以有多个父类。
class A {}
class B {}
class C1 extends A {} // ok
// class C2 extends A, B {} // error
- 一个类可以有多个子类。
// A可以有多个子类
class A {}
class C1 extends A {}
class C2 extends A {}
- 可以多层继承。
class A {}
class C1 extends A {}
class D extends C1 {}
顶层父类是Object类。所有的类默认继承Object,作为父类。
3. 7案例小结:
会写一个继承结构下的标准Javabean即可
需求:
猫:属性,姓名,年龄,颜色
狗:属性,姓名,年龄,颜色,吼叫
分享书写技巧:
1.在大脑中要区分谁是父,谁是子
2.把共性写到父类中,独有的东西写在子类中
3.开始编写标准Javabean(从上往下写)
4.在测试类中,创建对象并赋值调用
代码示例:
package com.itheima.test4;
public class Animal {
//姓名,年龄,颜色
private String name;
private int age;
private String color;
public Animal() {
}
public Animal(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
//get和set方法
}
public class Cat extends Animal{
//因为猫类中没有独有的属性。
//所以此时不需要写私有的成员变量
//空参
public Cat() {
}
//需要带子类和父类中所有的属性
public Cat(String name, int age, String color) {
super(name,age,color);
}
}
public class Dog extends Animal{
//Dog :吼叫
private String wang;
//构造
public Dog() {
}
//带参构造:带子类加父类所有的属性
public Dog(String name, int age, String color,String wang) {
//共性的属性交给父类赋值
super(name,age,color);
//独有的属性自己赋值
this.wang = wang;
}
public String getWang() {
return wang;
}
public void setWang(String wang) {
this.wang = wang;
}
}
public class Demo {
public static void main(String[] args) {
//Animal : 姓名,年龄,颜色
//Cat :
//Dog :吼叫
//创建狗的对象
Dog d = new Dog("旺财",2,"黑色","嗷呜~~");
System.out.println(d.getName()+", " + d.getAge() + ", " + d.getColor() + ", " + d.getWang());
//创建猫的对象
Cat c = new Cat("中华田园猫",3,"黄色");
System.out.println(c.getName() + ", " + c.getAge() + ", " + c.getColor());
}
}
第四章 多态
4.1 多态的形式
多态是继封装、继承之后,面向对象的第三大特性。
多态是出现在继承或者实现关系中的。
多态体现的格式:
父类类型 变量名 = new 子类/实现类构造器;
变量名.方法名();
多态的前提:
- 有继承关系,子类对象是可以赋值给父类类型的变量。例如Animal是一个动物类型,而Cat是一个猫类型。Cat继承了Animal,Cat对象也是Animal类型,自然可以赋值给父类类型的变量。
- 有继承/实现关系
- 有父类引用指向子类对象
- 有方法的重写
多态的好处:
- 使用父类型作为参数,可以接受所有子类对象
- 体现多态的扩展性与便利
4.2 多态的运行
4.2.1 多态的运行特点
调用成员变量时:编译看左边,运行看左边
调用成员方法时:编译看左边,运行看右边
代码示例:
Fu f = new Zi();
//编译看左边的父类中有没有name这个属性,没有就报错
//在实际运行的时候,把父类name属性的值打印出来
System.out.println(f.name);
//编译看左边的父类中有没有show这个方法,没有就报错
//在实际运行的时候,运行的是子类中的show方法
f.show();
4.2.2 多态的使用场景
父类:
public class Person {
private String name;
private int age;
空参构造
带全部参数的构造
get和set方法
public void show(){
System.out.println(name + ", " + age);
}
}
子类1:
public class Administrator extends Person {
@Override
public void show() {
System.out.println("管理员的信息为:" + getName() + ", " + getAge());
}
}
子类2:
public class Student extends Person{
@Override
public void show() {
System.out.println("学生的信息为:" + getName() + ", " + getAge());
}
}
子类3:
public class Teacher extends Person{
@Override
public void show() {
System.out.println("老师的信息为:" + getName() + ", " + getAge());
}
}
测试类:
public class Test {
public static void main(String[] args) {
//创建三个对象,并调用register方法
Student s = new Student();
s.setName("张三");
s.setAge(18);
Teacher t = new Teacher();
t.setName("王建国");
t.setAge(30);
Administrator admin = new Administrator();
admin.setName("管理员");
admin.setAge(35);
register(s);
register(t);
register(admin);
}
//这个方法既能接收老师,又能接收学生,还能接收管理员
//只能把参数写成这三个类型的父类
public static void register(Person p){
p.show();
}
}
4.2.3 引用类型转换:
- 向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。
一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。
使用格式:
子类类型 变量名 = (子类类型) 父类变量名;
如:Aniaml a = new Cat();
Cat c =(Cat) a;
instanceof关键字
为了避免ClassCastException的发生,Java提供了 instanceof
关键字,给引用变量做类型的校验,格式如下:
变量名 instanceof 数据类型
如果变量属于该数据类型或者其子类类型,返回true。
如果变量不属于该数据类型或者其子类类型,返回false。
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
if (a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
} else if (a instanceof Dog){
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse
}
}
}
instanceof新特性
JDK14的时候提出了新特性,把判断和强转合并成了一行
//新特性
//先判断a是否为Dog类型,如果是,则强转成Dog类型,转换之后变量名为d
//如果不是,则不强转,结果直接是false
if(a instanceof Dog d){
d.lookHome();
}else if(a instanceof Cat c){
c.catchMouse();
}else{
System.out.println("没有这个类型,无法转换");
}
代码
public class Test {
public static void main(String[] args) {
//创建对象
Animal a = new Dog();
//编译看左边,运行看右边
a.eat();
//多态的弊端
//不能调用子类的特有功能
//报错的原因?
//当调用成员方法的时候,编译看左边,运行看右边
//那么在编译的时候会先检查左边的父类中有没有这个方法,如果没有直接报错。
//a.lookHome();
//解决方案:
//变回子类类型就可以了
//细节:转换的时候不能瞎转,如果转成其他类的类型,就会报错
//Cat c = (Cat) a;
//c.catchMouse();
if(a instanceof Dog){
Dog d = (Dog) a;
d.lookHome();
}else if(a instanceof Cat){
Cat c = (Cat) a;
c.catchMouse();
}else{
System.out.println("没有这个类型,无法转换");
}
// //新特性
// //先判断a是否为Dog类型,如果是,则强转成Dog类型,转换之后变量名为d
// //如果不是,则不强转,结果直接是false
// if(a instanceof Dog d){
// d.lookHome();
// }else if(a instanceof Cat c){
// c.catchMouse();
// }else{
// System.out.println("没有这个类型,无法转换");
// }
}
}
class Animal{
public void eat(){
System.out.println("动物在吃东西");
}
}
class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃骨头");
}
public void lookHome(){
System.out.println("狗看家");
}
}
class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃小鱼干");
}
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
4.3 小结
1.多态的优势
方法中,使用父类型作为参数,可以接收所有子类对象
2.多态的弊端是什么?
不能使用子类的特有功能
3.引用数据类型的类型转换,有几种方式?
自动类型转换、强制类型转换
Person p = new Student ();
student s = (student)p;
4.强制类型转换能解决什么问题?
- 可以转换成真正的子类类型,从而调用子类独有功能。
- 转换类型与真实对象类型不一致会报错
- 转换的时候用instanceof关键字进行判断
4.4 案例
需求:根据需求完成代码:
1.定义狗类
属性:
年龄,颜色
行为:
eat(String something)(something表示吃的东西)
看家lookHome方法(无参数)
2.定义猫类
属性:
年龄,颜色
行为:
eat(String something)方法(something表示吃的东西)
逮老鼠catchMouse方法(无参数)
3.定义Person类//饲养员
属性:
姓名,年龄
行为:
keepPet(Dog dog,String something)方法
功能:喂养宠物狗,something表示喂养的东西
行为:
keepPet(Cat cat,String something)方法
功能:喂养宠物猫,something表示喂养的东西
生成空参有参构造,set和get方法
4.定义测试类(完成以下打印效果):
keepPet(Dog dog,String somethind)方法打印内容如下:
年龄为30岁的老王养了一只黑颜色的2岁的狗
2岁的黑颜色的狗两只前腿死死的抱住骨头猛吃
keepPet(Cat cat,String somethind)方法打印内容如下:
年龄为25岁的老李养了一只灰颜色的3岁的猫
3岁的灰颜色的猫眯着眼睛侧着头吃鱼
5.思考:
1.Dog和Cat都是Animal的子类,以上案例中针对不同的动物,定义了不同的keepPet方法,过于繁琐,能否简化,并体会简化后的好处?
2.Dog和Cat虽然都是Animal的子类,但是都有其特有方法,能否想办法在keepPet中调用特有方法?
//动物类(父类)
public class Animal {
private int age;
private String color;
public Animal() {
}
public Animal(int age, String color) {
this.age = age;
this.color = color;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public void eat(String something){
System.out.println("动物在吃" + something);
}
}
//猫类(子类)
public class Cat extends Animal {
public Cat() {
}
public Cat(int age, String color) {
super(age, color);
}
@Override
public void eat(String something) {
System.out.println(getAge() + "岁的" + getColor() + "颜色的猫眯着眼睛侧着头吃" + something);
}
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
//狗类(子类)
public class Dog extends Animal {
public Dog() {
}
public Dog(int age, String color) {
super(age, color);
}
//行为
//eat(String something)(something表示吃的东西)
//看家lookHome方法(无参数)
@Override
public void eat(String something) {
System.out.println(getAge() + "岁的" + getColor() + "颜色的狗两只前腿死死的抱住" + something + "猛吃");
}
public void lookHome(){
System.out.println("狗在看家");
}
}
//饲养员类
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//饲养狗
/* public void keepPet(Dog dog, String something) {
System.out.println("年龄为" + age + "岁的" + name + "养了一只" + dog.getColor() + "颜色的" + dog.getAge() + "岁的狗");
dog.eat(something);
}
//饲养猫
public void keepPet(Cat cat, String something) {
System.out.println("年龄为" + age + "岁的" + name + "养了一只" + cat.getColor() + "颜色的" + cat.getAge() + "岁的猫");
cat.eat(something);
}*/
//想要一个方法,能接收所有的动物,包括猫,包括狗
//方法的形参:可以写这些类的父类 Animal
public void keepPet(Animal a, String something) {
if(a instanceof Dog d){
System.out.println("年龄为" + age + "岁的" + name + "养了一只" + a.getColor() + "颜色的" + a.getAge() + "岁的狗");
d.eat(something);
}else if(a instanceof Cat c){
System.out.println("年龄为" + age + "岁的" + name + "养了一只" + c.getColor() + "颜色的" + c.getAge() + "岁的猫");
c.eat(something);
}else{
System.out.println("没有这种动物");
}
}
}
//测试类
public class Test {
public static void main(String[] args) {
//创建对象并调用方法
/* Person p1 = new Person("老王",30);
Dog d = new Dog(2,"黑");
p1.keepPet(d,"骨头");
Person p2 = new Person("老李",25);
Cat c = new Cat(3,"灰");
p2.keepPet(c,"鱼");*/
//创建饲养员的对象
Person p = new Person("老王",30);
Dog d = new Dog(2,"黑");
Cat c = new Cat(3,"灰");
p.keepPet(d,"骨头");
p.keepPet(c,"鱼");
}
}
第五章 抽象类
1.抽象类的作用是什么样的?
抽取共性时,无法确定方法体,就把方法定义为抽象的。强制让子类按照某种格式重写。
抽象方法所在的类,必须是抽象类。
2.继承抽象类有哪些要注意?
- 要么重写抽象类中的所有抽象方法
- 要么是抽象类
3. 抽象类和抽象方法的注意事项
- 抽象类不能实例化
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类可以有构造方法
- 抽象类的子类
- 要么重写抽象类中的所有抽象方法
- 要么是抽象类
5.1 abstract使用格式
abstract是抽象的意思,用于修饰方法方法和类,修饰的方法是抽象方法,修饰的类是抽象类。
抽象方法
使用abstract
关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体。
定义格式:
修饰符 abstract 返回值类型 方法名 (参数列表);
//代码举例:
public abstract void run();
抽象类
如果一个类包含抽象方法,那么该类必须是抽象类。注意:抽象类不一定有抽象方法,但是有抽象方法的类必须定义成抽象类。
定义格式:
abstract class 类名字 {
}
//代码举例:
public abstract class Animal {
public abstract void run();
}
抽象类的使用
要求:继承抽象类的子类必须重写父类所有的抽象方法。否则,该子类也必须声明为抽象类。
5.2 抽象类的细节
不需要背,只要当idea报错之后,知道如何修改即可。
关于抽象类的使用,以下为语法上要注意的细节,虽然条目较多,但若理解了抽象的本质,无需死记硬背。
-
抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。
-
抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。
-
抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。
-
抽象类的子类,必须重写抽象父类中所有的抽象方法,否则子类也必须定义成抽象类,编译无法通过而报错。
理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。
-
抽象类存在的意义是为了被子类继承。
理解:抽象类中已经实现的是模板中确定的成员,抽象类不确定如何实现的定义成抽象方法,交给具体的子类去实现。
5.3 案例
public abstract class Person {
private String name;
private int age;
//作用:当创建子类对象的时,给属性进行赋值的。
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//get和set方法
//抽象方法
public abstract void work();
public void sleep(){
System.out.println("睡觉");
}
}
public abstract class Teacher extends Person{
}
public class Student extends Person{
//构造方法
public Student() {
}
public Student(String name, int age) {
super(name, age); //赋值父类
}
@Override
public void work() {
System.out.println("学生的工作是学习");
}
}
public class ATeacher extends Teacher{
@Override
public void work() {
}
}
public class Test {
public static void main(String[] args) {
//创建对象
// 创建抽象类,抽象类不能创建对象
//Person p = new Person();
Student s = new Student("zhangsan",23);
System.out.println(s.getName() + ", " + s.getAge());
}
}
第六章 接口
6.1 接口的定义和使用
-
接口用关键字interface来定义
public interface 接口名{}
-
接口不能实例化(创建对象)
-
接口和类之间是实现关系,通过implements关键字表示
public class 类名 implements 接口名{}
- 接口的子类(实现类)
- 要么重写接口中的所有抽象方法
- 要么是抽象类
**注意1:**接口和类的实现关系,可以单实现,也可以多实现。
public class 类名 implements 接口名1,接口名2{}
**注意2:**实现类还可以在继承一个类的同时实现多个接口。
public class 类名 extends 父类 implements 接口名1,接口名2{}
6.2 定义格式
//接口的定义格式:
interface 接口名称{
// 抽象方法
}
// 接口的声明:interface
// 接口名称:首字母大写,满足“驼峰模式”
/**接口的实现:
在Java中接口是被实现的,实现接口的类称为实现类。
实现类的格式:*/
class 类名 implements 接口1,接口2,接口3...{
}
6.3 接口成员的特点
public interface InterF {
// 抽象方法!
// public abstract void run();
void run();
// public abstract String getName();
String getName();
// public abstract int add(int a , int b);
int add(int a , int b);
// 它的最终写法是:
// public static final int AGE = 12 ;
int AGE = 12; //常量
String SCHOOL_NAME = "黑马程序员";
}
6.4 接口与类之间的关系
-
类和类的关系
继承关系,只能单继承,不能多继承,但是可以多层继承 -
类和接口的关系
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
-
接口和接口的关系
继承关系,可以单继承,也可以多继承
public interface Abc {
void go();
void test();
}
/** 法律规范:接口*/
public interface Law {
void rule();
void test();
}
*
* 总结:
* 接口与类之间是多实现的。
* 接口与接口之间是多继承的。
* */
public interface SportMan extends Law , Abc {
void run();
}
6.5 案例:练习
练习编写带有接口和抽象类的标准Javabean类
我们现在有乒乓球运动员和篮球运动员,乒乓球教练和篮球教练。为了出国交流,跟乒乓球相关的人员都需要学习英语。
请用所有知识分析,在这个案例中,哪些是具体类,哪些是抽象类,哪些是接口?
乒乓球运动员:姓名,年龄,学打乒乓球,说英语
篮球运动员:姓名,年龄,学打篮球
乒乓球教练:姓名,年龄,教打乒乓球,说英语
篮球教练:姓名,年龄,教打篮球
方法一:
方法二:我们采用的
代码复现:
父类:
//因为现在我不想让外界去直接创建人的对象
//因为直接创建顶层父类人的对象此时是没有意义的
//所以我就把他写为抽象的。
public abstract class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//set和get方法
}
第二层
接口:
public interface English {
public abstract void speakEnglish();
//如果implement这个接口后,接口中的方法必须全部重载,要不然或报错
//public void speak1(); //=public abstract void speak1();
}
javabean类
//运动员类
public abstract class Sporter extends Person{
public Sporter() {
}
public Sporter(String name, int age) {
super(name, age);
}
public abstract void study();
}
//教练类
public abstract class Coach extends Person{
public Coach() {
}
public Coach(String name, int age) {
super(name, age);
}
public abstract void teach();
}
//乒乓球运动员
public class PingPangSporter extends Sporter implements English{
public PingPangSporter() {
}
public PingPangSporter(String name, int age) {
super(name, age);
}
@Override
public void speakEnglish() {
System.out.println("乒乓球运动员在说英语");
}
@Override
public void study() {
System.out.println("乒乓球运动员在学习如何打乒乓球");
}
}
//乒乓球教练
public class PingPangCoach extends Coach implements English{
public PingPangCoach() {
}
public PingPangCoach(String name, int age) {
super(name, age);
}
@Override
public void teach() {
System.out.println("乒乓球教练正在教如何打乒乓球");
}
@Override
public void speakEnglish() {
System.out.println("乒乓球教练在学习说英语");
}
}
//
public class BasketballCoach extends Coach{
public BasketballCoach() {
}
public BasketballCoach(String name, int age) {
super(name, age);
}
@Override
public void teach() {
System.out.println("篮球教练正在教如何打篮球");
}
}
//
public class BasketballSporter extends Sporter{
public BasketballSporter() {
}
public BasketballSporter(String name, int age) {
super(name, age);
}
@Override
public void study() {
System.out.println("篮球运动员在学习如何打篮球");
}
}
测试:
public class Test {
public static void main(String[] args) {
//创建运动员或教练的对象
PingPangSporter pps = new PingPangSporter("刘诗雯",23);
System.out.println(pps.getName() + ", " + pps.getAge());
pps.study();
pps.speakEnglish();
}
}
6.6 JDK8开始接口中新增的方法
- JDK7以前:接口中只能定义抽象方法。
- JDK8:接口中可以定义有方法体的方法。(默认、静态)
- JDK9:接口中可以定义私有方法。
- 私有方法分为两种:普通的私有方法,静态的私有方法
6.6.1 新增的方法
-
允许在接口中定义默认方法,需要使用关键字default修饰
作用:解决接口升级的问题
-
接口中默认方法的定义格式:
格式: public default 返回值类型 方法名(参数列表){}
范例: public default void show(){ }
- 接口中默认方法的注意事项:
- 默认方法不是抽象方法,所以不强制被重写。但是如果被重写,重写的时候去掉default关键字
- public可以省略,default不能省略
- 如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写
案例一:接口的方法不重写
//接口A
public interface InterA {
public abstract void method();
public default void show(){
System.out.println("InterA接口中的默认方法 ---- show");
}
}
//javabean类
public class InterImpl implements InterA{
@Override
public void method() {
System.out.println("实现类重写的抽象方法");
}
}
//实现类
public class Test {
public static void main(String[] args) {
//创建实现类的对象
InterImpl ii = new InterImpl();
ii.method();
ii.show();
}
}
//输出
实现类重写的抽象方法
InterA接口中的默认方法 ---- show
案例二:接口的方法重写
//接口A
public interface InterA {
public abstract void method();
public default void show(){
System.out.println("InterA接口中的默认方法 ---- show");
}
}
//接口b
public interface InterB {
public default void show(){
System.out.println("InterB接口中的默认方法 ---- show");
}
}
//javabean类
public class InterImpl implements InterA{
@Override
public void method() {
System.out.println("实现类重写的抽象方法");
}
//重写的时候去掉default关键字
@Override
public void show() {
System.out.println("重写接口中的默认方法");
}
}
//实现:测试
public class Test {
public static void main(String[] args) {
/*接口中默认方法的定义格式:
格式:public default 返回值类型 方法名(参数列表) { }
接口中默认方法的注意事项:
1.默认方法不是抽象方法,所以不强制被重写。但是如果被重写,重写的时候去掉default关键字
2.public可以省略,default不能省略
3.如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写
*/
//创建实现类的对象
InterImpl ii = new InterImpl();
ii.method();
ii.show();
}
}
//输出
实现类重写的抽象方法
重写接口中的默认方法
6.6.2 新增的静态方法
-
允许在接口中定义静态方法,需要使用关键字static修饰
-
接口中静态方法的定义格式:
格式: public static 返回值类型 方法名(参数列表){}
范例: public static void show(){ }
- 接口中静态方法的注意事项:
- 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
- public可以省略,static不能省略
案例
//接口
public interface Inter {
public abstract void method();
public static void show(){
System.out.println("Inter接口中的静态方法");
}
}
//javabean类
public class InterImpl implements Inter{
@Override
public void method() {
System.out.println("InterImpl重写的抽象方法");
}
//不叫重写
public static void show() {
System.out.println("InterImpl重写的抽象方法");
}
}
public class Test {
public static void main(String[] args) {
//调用接口中的静态方法
Inter.show();
//调用实现类中的静态方法
InterImpl.show();
//子类把从父类继承下来的虚方法表里面的方法进行覆盖了,这才叫重写。
}
}
//输出
Inter接口中的静态方法
InterImpl重写的抽象方法
6.6.3 JDK9以后新增加的方法
public class Test {
public static void main(String[] args) {
/* 接口中私有方法的定义格式:
格式1:private 返回值类型 方法名(参数列表) { }
范例1:private void show() { }
格式2:private static 返回值类型 方法名(参数列表) { }
范例2:private static void method() { }
*/
}
}
public interface InterA {
public static void show1(){
System.out.println("show1方法开始执行了");
show4();
}
public static void show2(){
System.out.println("show2方法开始执行了");
show4();
}
//普通的私有方法,给默认方法服务的
private void show3(){
System.out.println("记录程序在运行过程中的各种细节,这里有100行代码");
}
//静态的私有方法,给静态方法服务的
private static void show4(){
System.out.println("记录程序在运行过程中的各种细节,这里有100行代码");
}
}
6.7 接口的应用
1.接口代表规则,是行为的抽象。想要让哪个类拥有一个行为,就让这个类实现对应的接口就可以了。
2.当一个方法的参数是接口时,可以传递接口所有实现类的对象,这种方式称之为接口多态。
6.8适配器设计模式
- 设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。
- 简单理解:设计模式就是各种套路。
- 适配器设计模式:解决接口与接口实现类之间的矛盾问题
1.当一个接口中抽象方法过多,但是我只要使用其中一部分的时候,就可以适配器设计模式
2.书写步骤:
- 编写中间类XXXAdapter,实现对应的接口
- 对接口中的抽象方法进行空实现
- 让真正的实现类继承中间类,并重写需要用的方法
- 为了避免其他类创建适配器类的对象,中间的适配器类用abstract进行修饰
//接口
public interface Inter {
public abstract void method1();
public abstract void method2();
public abstract void method3();
public abstract void method4();
public abstract void method5();
public abstract void method6();
public abstract void method7();
public abstract void method8();
public abstract void method9();
public abstract void method10();
}
//中间类
public abstract class InterAdapter implements Inter{
@Override
public void method1() {
}
@Override
public void method2() {
}
@Override
public void method3() {
}
@Override
public void method4() {
}
@Override
public void method5() {
}
@Override
public void method6() {
}
@Override
public void method7() {
}
@Override
public void method8() {
}
@Override
public void method9() {
}
@Override
public void method10() {
}
}
实现类
public class InterImpl extends InterAdapter{
//我需要用到那个方法,就重写哪个方法就可以了
@Override
public void method5() {
System.out.println("只要用第五个方法");
}
}
第七章 内部类
**类的五大成员:**属性、方法、构造方法、代码块、内部类
7.1 概述
public class Car {
String carName;
int carAge;
String carColor;
public void show(Car this){
//是打印调用者车的名字:宾利
System.out.println(this.carName);
//调用内部类
Engine e = new Engine();
System.out.println(e.engineName);
}
//内部类
class Engine{
String engineName;
int engineAge;
public void show(){
System.out.println(engineName);
System.out.println(carName);
}
}
}
7.2 内部类的分类
按定义的位置来分
- 成员内部类,类定义在了成员位置 (类中方法外称为成员位置,无static修饰的内部类)
- 静态内部类,类定义在了成员位置 (类中方法外称为成员位置,有static修饰的内部类)
- 局部内部类,类定义在方法内
- 匿名内部类,没有名字的内部类,可以在方法中,也可以在类中方法外。 (掌握)
7.3 成员内部类
1.成员内部类的代码如何书写
2.如何创建成员内部类的对象
3.成员内部类如何获取外部类的成员变量
成员内部类:
-
写在成员位置的,属于外部类的成员。
-
成员内部类可以被一些修饰符所修饰,比如: private,默认,protected,public,static等在成员内部类里面,
-
JDK16之前不能定义静态变量,JDK 16开始才可以定义静态变量。
public class car {外部类 string carName; int carAge; int carcolor; //成员内部类 class Engine{ string engineName; int engineAge; } }
成员内部类特点:
- 无static修饰的内部类,属于外部类对象的。
- 宿主:外部类对象。
内部类的使用格式:
外部类.内部类。 // 访问内部类的类型都是用 外部类.内部类
获取成员内部类对象的两种方式:
方式一:外部直接创建成员内部类的对象
外部类.内部类 变量 = new 外部类().new 内部类();
方式二:在外部类中定义一个方法提供内部类的对象
方式一:
public class Test {
public static void main(String[] args) {
// 宿主:外部类对象。
// Outer out = new Outer();
// 创建内部类对象。
Outer.Inner oi = new Outer().new Inner();
oi.method();
}
}
class Outer {
// 成员内部类,属于外部类对象的。
// 拓展:成员内部类不能定义静态成员。
public class Inner{
// 这里面的东西与类是完全一样的。
public void method(){
System.out.println("内部类中的方法被调用了");
}
}
}
方式二:
public class Outer {
String name;
private class Inner{
static int a = 10;
}
public Inner getInstance(){
return new Inner();
}
}
public class Test {
public static void main(String[] args) {
Outer o = new Outer();
System.out.println(o.getInstance());
}
}
成员内部类的细节
编写成员内部类的注意点:
- 成员内部类可以被一些修饰符所修饰,比如: private,默认,protected,public,static等
- 在成员内部类里面,JDK16之前不能定义静态变量,JDK16开始才可以定义静态变量。
- 创建内部类对象时,对象中有一个隐含的Outer.this记录外部类对象的地址值。(请参见3.6节的内存图)
详解:
内部类被private修饰,外界无法直接获取内部类的对象,只能通过3.3节中的方式二获取内部类的对象
被其他权限修饰符修饰的内部类一般用3.3节中的方式一直接获取内部类的对象
内部类被static修饰是成员内部类中的特殊情况,叫做静态内部类下面单独学习。
内部类如果想要访问外部类的成员变量,外部类的变量必须用final修饰,JDK8以前必须手动写final,JDK8之后不需要手动写,JDK默认加上
7.4 成员内部类面试题
请在?地方向上相应代码,以达到输出的内容
注意:内部类访问外部类对象的格式是:外部类名.this
public class Test {
public static void main(String[] args) {
Outer.inner oi = new Outer().new inner();
oi.method();
}
}
class Outer { // 外部类
private int a = 30;
// 在成员位置定义一个类
class inner {
private int a = 20;
public void method() {
int a = 10;
System.out.println(???); // 10 答案:a
System.out.println(???); // 20 答案:this.a
System.out.println(???); // 30 答案:Outer.this.a
}
}
}
7.5 静态内部类
静态内部类特点:
-
静态内部类是一种特殊的成员内部类。
-
有static修饰,属于外部类本身的。
-
总结:静态内部类与其他类的用法完全一样。只是访问的时候需要加上外部类.内部类。
-
拓展1:静态内部类可以直接访问外部类的静态成员。
-
拓展2:静态内部类不可以直接访问外部类的非静态成员,如果要访问需要创建外部类的对象。
-
拓展3:静态内部类中没有银行的Outer.this。
内部类的使用格式:
外部类.内部类。
静态内部类对象的创建格式:
外部类.内部类 变量 = new 外部类.内部类构造器;
调用方法的格式:
- 调用非静态方法的格式:先创建对象,用对象调用
- 调用静态方法的格式:外部类名.内部类名.方法名();
案例演示:
// 外部类:Outer01
class Outer01{
private static String sc_name = "黑马程序";
// 内部类: Inner01
public static class Inner01{
// 这里面的东西与类是完全一样的。
private String name;
public Inner01(String name) {
this.name = name;
}
public void showName(){
System.out.println(this.name);
// 拓展:静态内部类可以直接访问外部类的静态成员。
System.out.println(sc_name);
}
}
}
public class InnerClassDemo01 {
public static void main(String[] args) {
// 创建静态内部类对象。
// 外部类.内部类 变量 = new 外部类.内部类构造器;
Outer01.Inner01 in = new Outer01.Inner01("张三");
in.showName();
}
}
7.6 局部内部类
- 局部内部类 :定义在方法中的类。
局部内部类
1.将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量。
2.外界是无法直接使用局部内部类,需要在方法内部创建对象并使用。
3.该类可以直接访问外部类的成员,也可以访问方法内的局部变量。
定义格式:
class 外部类名 {
数据类型 变量名;
修饰符 返回值类型 方法名(参数列表) {
// …
class 内部类 {
// 成员变量
// 成员方法
}
}
}
public class Outer {
int b = 20;
public void show(){
int a = 10;
//局部内部类
class Inner{
String name;
int age;
public void method1(){
System.out.println(a);
System.out.println(b);
System.out.println("局部内部类中的method1方法");
}
public static void method2(){
System.out.println("局部内部类中的method2静态方法");
}
}
//创建局部内部类的对象
Inner i = new Inner();
System.out.println(i.name);
System.out.println(i.age);
i.method1();
Inner.method2();
}
}
public class Test {
public static void main(String[] args) {
/*
局部内部类
1.将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量。
2.外界是无法直接使用局部内部类,需要在方法内部创建对象并使用。
3.该类可以直接访问外部类的成员,也可以访问方法内的局部变量。*/
//调用show方法,让代码执行
Outer o = new Outer();
o.show();
}
}
7.7 匿名内部类
1.什么是匿名内部类?
隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置。
2.格式的细节
包含了继承或实现,方法重写,创建对象。
整体就是一个类的子类对象或者接口的实现类对象
3.使用场景
当方法的参数是接口或者类时,
以接口为例,可以传递这个接口的实现类对象,
如果实现类只要使用一次,就可以用匿名内部类简化代码。
案例
//接口
public interface Swim {
public abstract void swimming();
}
//实现1
public class Demo07 {
public static void main(String[] args) {
new Swim() {
@Override
public void swimming() {
System.out.println("自由泳...");
}
}.swimming();
}
}
//输出
自由泳...
//实现2
public class Demo07 {
public static void main(String[] args) {
// 接口 变量 = new 实现类(); // 多态,走子类的重写方法
Swim s2 = new Swim() {
@Override
public void swimming() {
System.out.println("蛙泳...");
}
};
s2.swimming();
}
}
//输出
蛙泳...
案例2
interface Swim {
public abstract void swimming();
}
public class Demo07 {
public static void main(String[] args) {
// 普通方式传入对象
// 创建实现类对象
Student s = new Student();
goSwimming(s);
// 匿名内部类使用场景:作为方法参数传递
Swim s3 = new Swim() {
@Override
public void swimming() {
System.out.println("蝶泳...");
}
};
// 传入匿名内部类
goSwimming(s3);
// 完美方案: 一步到位
goSwimming(new Swim() {
public void swimming() {
System.out.println("大学生, 蛙泳...");
}
});
goSwimming(new Swim() {
public void swimming() {
System.out.println("小学生, 自由泳...");
}
});
}
// 定义一个方法,模拟请一些人去游泳
public static void goSwimming(Swim s) {
s.swimming();
}
}