“我曾经被问到‘求教,Baddage先生,如果你向机器中输入错误的数字,可以得到正确的答案吗?’ 我无法恰当地理解产生这种问题的概念上的混淆。”
--Charles Babbage(1791-1871)
1、多态的概述
多态方法的调用允许一种类型表现处与其他相似类型之间的区别,只要他们是从同一个基类中导出而来的。这种区别是根据方法行为的不同而表现出来的,虽然这些方法都可以通过同一个基类来调用。
简单的说,某一个事物在不同状态下的多种状态
2、实现多态的三大前提
- 必须要有继承关系
- 要有方法的重写(思考可否不重写?)
不是必须要重写的,重写可以体现子类的专属特征。
- 要有父类的引用指向子类对象
使用多态第三个前提的注意事项:
1.必须是继承关系的类,才可以写成父类的引用指向子类对象
2.左边的父类,可以不必是子类对象的直接父类,也可以是父类的父类
代码体现
package day09;
class Demo1{
}
class Power{
}
class Water extends Power{
public void getPower(){
System.out.println("提供能量");
}
}
class GutaiWater extends Water{
@Override
public void getPower(){
System.out.println("可以嚼着吃");
}
}
public class DuoTaiDemo1 {
public static void main(String[] args) {
//Demo1 w1 = new GutaiWater();
// 错误的,Demo1类与GutaiWater类没有继承关系
Water w2 = new GutaiWater();//从右往左读,固态的水是水
Power p1 = new GutaiWater();
}
}
3、访问成员的特点
多态下,访问成员的特点:
1、成员变量
编译看左,允许看左。
2、成员方法
编译看左,运行看右。
思考:如何在多态的状态下,使用子类中特有的方法呢?
3、静态的成员方法
编译看左,运行看左。
package day09;
class Fu1{
int a1 = 10;
public void fun1(){
System.out.println("这是父类中的非静态方法fun1");
}
public static void fun2(){
System.out.println("这是父类中的静态方法fun2");
}
}
class Zi1 extends Fu1{
int a = 11;
@Override
public void fun1(){
System.out.println("子类中重写的非静态方法fun1");
}
public void show1(){
System.out.println("天天向上,好好学习!");
}
public static void fun2(){
System.out.println("这是子类中的静态方法fun2");
}
}
public class DuoTaiDemo2 {
public static void main(String[] args) {
//使用多态的方式创建一个子类对象
Fu1 f1 = new Zi1();
System.out.println(f1.a1); // 编译的时候,看左边父类中是否有该变量存在,若存在,编译不报错,运行的结果也是取父类中该变量的值。
f1.fun1();// 编译的时候,看左边父类中是否有该方法存在,若存在,编译不报错,运行的结果也是取子类的中该方法的实现。
// f1.show1();
// f1.fun2(); // 静态的成员方法是跟随类走的,什么类型的变量调用静态方法,就是该类中的静态方法
Fu1.fun2();
// Zi1 zi1 = new Zi1();
// System.out.println(zi1.a1);
}
}
4、多态的好处
1.提高了程序的维护性(继承)
2.提高了程序的扩展性(多态)
可以从通用的基类继承出新的数据类型,从而新添加一些功能,而那些操纵基类接口的方法不需要任何改动就可以应用于新类。
package day09;
import java.nio.channels.Pipe;
/*
多态的好处
1、提高了程序的维护性(由继承保证)
2、提高了程序的扩展性(由多态保证)
*/
class Animal{
String name;
int age;
public Animal() {
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public void eat(){
System.out.println("吃");
}
public void sleep(){
System.out.println("睡");
}
}
class Dog extends Animal{
@Override
public void eat() {
System.out.println("🐕吃🥩");
}
@Override
public void sleep() {
System.out.println("🐕侧着睡");
}
}
class Pig extends Animal{
@Override
public void eat() {
System.out.println("🐖吃杂食");
}
@Override
public void sleep() {
System.out.println("🐖趴着睡");
}
}
class Snake extends Animal{
@Override
public void eat() {
System.out.println("🐍吃🐀");
}
@Override
public void sleep() {
System.out.println("🐍盘着睡");
}
}
class Tiger extends Animal{
@Override
public void eat() {
System.out.println("🐅吃🥩");
}
@Override
public void sleep() {
System.out.println("🐅趴着睡");
}
}
//写一个工具类,对每一个动物调用吃和睡的功能
class AnimalTool{
private AnimalTool(){}
// public static void useDog(Dog dog){
// dog.eat();
// dog.sleep();
// }
//
// public static void usePig(Pig pig){
// pig.eat();
// pig.sleep();
// }
//
// public static void useSnake(Snake snake){
// snake.eat();
// snake.sleep();
// }
public static void useAnimal(Animal animal){ //Animal animal = new Tiger();
animal.eat();
animal.sleep();
}
}
public class DuoTaiDemo5 {
public static void main(String[] args) {
//我想一只🐕
Dog d1 = new Dog();
// d1.eat();
// d1.sleep();
//使用工具类的方式调用动物的吃和睡的功能
// AnimalTool.useDog(d1);
AnimalTool.useAnimal(d1);
//我想一只🐖
Pig p2 = new Pig();
// p2.eat();
// p2.sleep();
// AnimalTool.usePig(p2);
AnimalTool.useAnimal(p2);
//我想养🐍
//按照上面的写法,应该先写一个🐍的类,然后在类中重写eat和sleep方法
//在工具类中添加一个调用🐍的吃和睡的方法
Snake s1 = new Snake();
// AnimalTool.useSnake(s1);
AnimalTool.useAnimal(s1);
//随着我们要养动物的种类越来越多,每一种动物都需要经过上面两个步骤
//创建该动物的类我们能理解,这是必须要有的
//但是第二步在工具类进行添加修改其实是有问题。
//今后的开发中,工具类是不允许频繁修改的。
//但是我们不添加方法的话,就没法使用工具类调用具体动物的功能
//于是,我们就在想,怎么可以只在工具类中写一个方法,可以调用所有动物的功能呢?
//可以使用多态来实现。
//我现在养一只🐅
Tiger t1 = new Tiger();
AnimalTool.useAnimal(t1); // new Tiger()
}
}
5、多态的弊端
弊端:在多态的形式下,无法使用子类中特有的成员方法
解决方案:向下转型(曹操和曹植的故事)
曹操和曹植的故事,曹操是曹植的父亲,曹植是曹操的儿子
class 曹操{
public void skill(){
带兵打仗
}
}
class 曹植 extends 曹操{
@Override
public void skill(){
下棋
}
public void zuoShi(){
作诗
}
}
某一天,曹操带兵打仗出城了,城里只有曹植,这时候刘备待人过来攻打城池,但是小兵只听曹操的指令。
为了守护城池,曹植想到一个办法,装爹,粘上胡子,穿上爹的衣服,调用跟爹一样的skill方法。
//向上转型
曹操 c1 = new 曹植();
c1.skill();
// c1.zuoShi(); 不能调用
曹操打仗回来了,曹植看到父亲回来后,不用继续装爹,做回自己,脱掉爹的衣服,撕掉假胡子
//向下转型
曹植 c2 = (曹植) c1;
c2.skill();
c2.zuoShi();
代码展示:
package day09;
class Fu2{
public void fun1(){
System.out.println("Hello World");
}
}
class Zi2 extends Fu2{
@Override
public void fun1(){
System.out.println("Hello Java");
}
public void show1(){
System.out.println("编程有意思");
}
}
public class DuoTaiDemo3 {
public static void main(String[] args) {
Fu2 f1 = new Zi2();
f1.fun1();
// f1.show();//根据多态访问成员的特点,编译看左,父类中没有show1方法,编译报错。
//向下转型
Zi2 z1 = new Zi2();
z1.show1();
}
}
但是向下转型同样也会遇到问题,在Java中所有的转型都会得到检查,以便保证它是我们想得到的类型。如果不是,就会返回一个ClassCastException(类转型异常)。这种在运行期间对类型进行检查的行为被称为“运行时类型识别”(RTTI)。
6、抽象类
1、抽象方法
Java中提供一个叫做抽象方法的机制,这种方法是不完整的;仅有声明而没有方法体。
2、格式
针对抽象的事物,java也提供了一个关键字,可以将抽象的概念,定义成一个抽象的类,这个类将来不能进行new的。
这个关键字:abstract
abstract void f();
3、抽象类的概述
包含抽象方法的类叫做抽象类。如果一个类包含一个或多个抽象方法,该类必须被限定为抽象的。(否则,编译器就会报错。)
4、抽象类使用注意事项
- 抽象类不能进行实例化
- 抽象类中既可以存在具体的方法,也可以存在抽象方法,如果一个类中有抽象方法,这个类必须是抽象类
- 抽象类可以被具体的类继承,但是如果是被具体的类继承的话,必须要重写抽象类中的所有抽象方法
- 抽象类A继承抽象类B的时候,可以选择是否重写抽象方法,也可以都不重写,原因是上面的第2点
- 抽象类无法进行实例化,但是却可以拥有构造方法,意义是为了将来父类做初始化,子类构造方法第一句话默认是super()
- 抽象类中,可以存在变量,常量,静态的变量。
package day09
abstract class Animal{
int a = 10;
final int b = 20;
static int c = 30;
public Animal() {
}
public void eat1(){
System.out.println("吃");
}
//抽象方法
public abstract void eat2();
public abstract void fun1();
}
abstract class Animal1 extends Animal{
@Override
public void eat2(){
}
}
class Dog3 extends Animal{
@Override
public void eat2(){
System.out.println("吃肉");
}
@Override
public void fun1() {
System.out.println("好好学习,天天向上!");
}
}
public class AbstractDemo1 {
public static void main(String[] args) {
// Animal animal = new Animal();//创建对象为抽象类型,编译器会认为不安全,会得到一条出错信息。
Dog3 d1 = new Dog3();
d1.eat2();
d1.fun1();
Animal a1 = new Dog3();
a1.eat1();
a1.eat2();
a1.fun1();
System.out.println(d1.a);
System.out.println(d1.b);
System.out.println(Animal.c);
}
}
5、abstract不能和哪些关键字共存
- private 冲突
- final 冲突
- static 无意义
以后我们常见的修饰符组合:
1、public static
2、public abstract
package day09
abstract class Demo2{
// private abstract void fun1();//非法的修饰符组合:abstract和private
// private修饰的方法不能被子类继承,但是abstract方法将来是要被重写的,也冲突了。
// public abstract final void fun1();//非法的修饰符组合:abstrac
// public static abstract void fun1();//非法的修饰符组合:abstract和static
}
public class AbstractDemo3 {
public static void main(String[] args) {
}
}
7、接口
1、接口的概念
一个接口表示:“所有实现了该特定接口的类看起来都像这样”。因此,任何使用某特定接口的代码都知道可以调用给接口的哪些方法,而且仅需知道这些。因此,接口被用来建立类与类之间的协议(某些面向对象编程语言使用关键字protocol来完成这一功能。)
2、关键字interface
interface这个关键字会产生一个完全抽象的类,它根本就没有提供任何具体实现。它允许创建者确定方法名、参数列和返类型,但是没有任何方法体。接口只提供了形式,而未提供任何具体实现。
格式:
interface 接口名{
}
使用接口的注意事项:
- 接口中只能出现抽象方法,不能写具体的方法
- 一个具体的类实现接口的话,需要使用关键字:implements
- 若一个具体的类实现接口的时候,必须要实现接口中所有的抽象方法
- 一个具体的类可以同时实现多个接口,但是必须要所有的抽象方法
- 抽象也可以实现一个或多个接口
package day09
//定义一个骑车的接口
interface Cycling{
// public void qiChe(){
// System.out.println("骑车");
// }
public abstract void qiChe();
}
interface JiSuan{
public abstract void calculation();
}
abstract class Animal4 implements Cycling,JiSuan{
// public abstract void qiChe();
// public abstract void calculation();
}
//普通的狗狗
class Dog4{
}
//会骑车的狗狗
class QiCheDog4 implements Cycling, JiSuan{
@Override
public void qiChe() {
System.out.println("狗狗骑车");
}
@Override
public void calculation() {
System.out.println("狗狗会做高数");
}
}
public class InterfaceDemo1 {
public static void main(String[] args) {
QiCheDog4 q = new QiCheDog4();
q.calculation();
q.qiChe();
}
}
3、接口和接口之间是继承关系
一个可以继承若干个接口,可以多继承。
面试题:java中允许多继承吗?
1、类与类之间只允许单继承,不允许多继承,但是可以多层继承,形成继承体系
2、接口与接口允许多继承
package day09;
interface C1{
public abstract void fun5();
public abstract void fun6();
}
interface B1{
public abstract void fun2();
public abstract void fun3();
public abstract void fun4();
}
interface A1 extends B1,C1{
// public abstract void fun2();
// public abstract void fun3();
// public abstract void fun4();
public abstract void fun1();
}
class Demo3 implements A1{
@Override
public void fun1() {
}
@Override
public void fun2() {
}
@Override
public void fun3() {
}
@Override
public void fun4() {
}
@Override
public void fun5() {
}
@Override
public void fun6() {
}
}
public class InterfaceDemo2 {
public static void main(String[] args) {
}
}
4、接口中成员变量的特点
成员变量:
接口中的变量默认都是被添加修饰符:public static final
换句话说,接口中只允许出现常量
构造方法:
接口中没有构造方法,自己写都不行,接口不能实例化
成员方法:
接口只能是抽象方法,默认会在方法前加上修饰符:public abstract
package day09;
interface Inter1{
// Inter1(){
//
// }
public static final int a = 10;
public abstract void fun1();
}
class Inter1Impl implements Inter1{
public void fun1(){
// a=100;//java无法为最终变量a分配值
System.out.println(a);
}
}
public class InterfaceDemo2 {
public static void main(String[] args) {
Inter1Impl inter1 = new Inter1Impl();
inter1.fun1();;
System.out.println(Inter1.a);
// Inter1 inter2 = new Inter1();
}
}
interface StudentDao{
public abstract void insert();
public abstract void delete();
public abstract void update();
public abstract void select();
}
class StudentDaoImpl implements StudentDao {
@Override
public void insert(){
}
@Override
public void delete(){
}
@Override
public void update() {
}
@Override
public void select() {
}
}
5、特点总结
- 接口中只有常量,所有的变量将来默认会使用public static final进行修饰
- 接口中只允许出现抽象方法,所有的方法将来默认会使用public abstract进行修饰
- 具体类实现接口使用implements关键字,当具体的类实现接口的时候,必须要实现接口中所有的抽象方法
- 一个类可以同时实现多个不同接口
- 接口与接口之间的关系是继承关系,一个接口可以同时继承多个接口
- 接口无法实例化
8、形式参数和返回值类型对比
1、形式参数
基本类型:简单,不提
引用类型:
(1) 类:当你看到一个类作为方法参数类型的时候,将来调用时需要传递该类及其该类的子类对象
package day10
class Demo1{
public void fun1(){
System.out.println("好好学习,天天向上!");
}
}
class Demo1Zi extends Demo1{
}
class Demo1Test{
public void show(Demo1 demo1){//Demo1 demo1 = new Demo1Zi()
demo1.fun1();
}
}
public class StudentDemo1 {
public static void main(String[] args) {
Demo1Test demo1Test = new Demo1Test();
demo1Test.show(new Demo1());
demo1Test.show(new Demo1Zi());
}
}
(2) 抽象类:当你看到一个抽象类作为方法的参数类型的时候,将来调用时需要传递继承该抽象类的具体子类对象
package day10;
abstract class Demo2{
public abstract void fun1();
}
class Demo2Zi extends Demo2{
@Override
public void fun1(){
System.out.println("好好学习,天天向上!");
}
}
class Student2{
public void show(Demo2 demo2){//Demo2 demo2 = new Demo2Zi()
demo2.fun1();
}
}
public class StudentDemo2 {
public static void main(String[] args) {
Student2 student2 = new Student2();
student2.show(new Demo2Zi());
}
}
(3)接口:当你看到一个接口作为方法参数类型的时候,将来调用时需要传递实现该接口的具体子类对象
package day10;
//接口:当你看到一个接口作为方法参数类型的时候,将来调用时需要传递实现该接口的具体子类对象
interface Inter1{
public abstract void fun1();
}
class Inter1Impl implements Inter1{
@Override
public void fun1(){
System.out.println("好好学习,天天向上!");
}
}
class Student3{
public void show(Inter1 inter1){//Inter1 inter1 = new Inter1Ipml()接口多态
inter1.fun1();
}
}
public class StudentDemo3 {
public static void main(String[] args) {
Student3 student3 = new Student3();
student3.show(new Inter1Impl());
}
}
2、返回值类型对比
(1)类:当你看到一个类作为方法的返回值类型的时候,将来方法内部应该返回该类或该类的子类对象
package day10;
//类:当你看到一个类作为方法的返回值类型的时候,将来方法内部应该返回该类或该类的子类对象
class Student1{
public void fun1(){
System.out.println("好好学习,天天向上!");
}
}
class Demo1{
public Student1 show(){
return new Student1();
}
}
public class StudentDemo1 {
public static void main(String[] args) {
Demo1 demo1 = new Demo1();
Student1 s1 = demo1.show(); // Student1 s1 = new Student1()
s1.fun1();
System.out.println("s1: "+s1);
Student1 s2 = demo1.show();
System.out.println("s2: "+s2);
}
}
(2)抽象类:当你看到一个抽象类作为方法的返回值类型的时候,将来方法内部应该返回继承该抽象类的具体子类对象
package day10;
//抽象类:当你看到一个抽象类作为方法的返回值类型的时候,将来方法内部应该返回继承该抽象类子类对象
abstract class Demo2{
public abstract void fun1();
}
class Demo2Zi extends Demo2{
@Override
public void fun1() {
System.out.println("张三是阿里巴巴的高级开发程序员");
}
}
class Student2{
public Demo2 show(){
return new Demo2Zi();
}
}
public class StudentDemo2 {
public static void main(String[] args) {
Student2 student2 = new Student2();
Demo2 d1 = student2.show(); //Demo2 d1 = new Demo2Zi() 抽象多态
d1.fun1();
}
}
(3)接口:当你看到一个接口作为方法的返回值类型的时候,将来方法内部应该返回实现了该接口的具体子类对象
package day10;
//接口:当你看到一个接口作为方法的返回值类型的时候,将来方法内部应该返回实现了该接口的具体子类对象
interface Inter1{
public abstract void fun1();
}
class Inter1Impl implements Inter1{
@Override
public void fun1() {
System.out.println("昨天南京下冰雹了");
}
}
class Student3{
public Inter1 show(){
return new Inter1Impl();
}
}
public class StudentDemo3 {
public static void main(String[] args) {
Student3 student3 = new Student3();
Inter1 i1 = student3.show(); //Inter1 i1 = new Inter1Impl() 接口多态
i1.fun1();
// Student3 student4 = new Student3();
// Inter1 i2 = new Student3().show();
new Student3()
.show()
.fun1(); // 调用方式:链式调用
}
}
9、包
1、包的划分
- 根据功能进行划分
- 根据角色进行划分
包:其实就是文件夹(目录)
创建包的好处:
1、帮助我们管理代码文件
2、不同的包下名字重复
以后创建包的话常用的划分方式:
1、按照功能划分:
增加功能
学生增加
老师增加
删除功能
学生删除
老师删除
修改功能
学生修改
老师修改
查询功能
学生查询
老师查询
2、按照角色划分
学生
学生增加
学生删除
学生修改
学生查询
老师
老师增加
老师删除
老师修改
老师查询
若每一个维度会经常改变,就不要将其设计成包
//课外了解
spring boot开发中常用建包方式:
controller 控制类层(主要是与前端页面做交互的)
entity(pojo)实体类层
dao 数据库操作层
service 服务接口层
- serviceImpl 服务实现类层
2、导包
import 包名;
在package和所有的class之间写。
10、权限修饰符
常用的修饰符:
权限修饰符:public,protected,默认的,private
静态修饰符:static
常量修饰符:final
抽象修饰符:abstract
类:
权限修饰符:public,默认的
常量修饰符:final
抽象修饰符:abstract
成员变量:
权限修饰符:public,protected,默认的,private
静态修饰符:static
常量修饰符:final
构造方法:
权限修饰符:public,protected,默认的,private
成员方法:
权限修饰符:public,protected,默认的,private
静态修饰符:static
常量修饰符:final
抽象修饰符:abstract
public
public abstract
public static
public static final
package day10;
public class Student {
public String name;
protected int age;
String address;
private String phoneNum;
public void fun1(){
System.out.println(name);
System.out.println(age);
System.out.println(address);
System.out.println(phoneNum);
}
}
package day10;
public class StudentZi extends Student{
public void show1(){
System.out.println(name);
System.out.println(age);
System.out.println(address);
// System.out.println(phoneNum);
}
}
package day10;
import day10.Student;
public class StudentZi2 extends Student {
public void show2(){
System.out.println(name);
System.out.println(age);
// System.out.println(address);
// System.out.println(phoneNum);
}
}
package day10;
import day10.Student;
public class StudentDemo {
public void fun4(){
Student student = new Student();
System.out.println(student.name);
// System.out.println(student.age);
// System.out.println(student.address);
// System.out.println(student.phoneNum);
}
11、内部类
1、概述及分类
内部类:将一个类A定义在一个类B中,这个类A称之为内部类
分类:
成员内部类:将类定义在一个类中的成员位置上
局部内部类:将类定义在一个方法中
package day10;
class Outer1{
int a1 = 10;
private int a2 = 11;
public static int a3 = 12;
class Inner1{
public void fun1(){
a1 = 20;
System.out.println(a1);
a2 = 30;
System.out.println(a2);
a3 = 40;
System.out.println(a3);
}
}
}
public class InnerDemo1 {
public static void main(String[] args) {
//外部类类名.内部类类名 对象名 = new 外部类类名().new 内部类类名();
Outer1.Inner1 inner1 = new Outer1().new Inner1();
inner1.fun1();
}
}
2、格式
外部类类名.内部类类名 对象名 = new 外部类类名().new 内部类类名()。
3、内部类的修饰符
(1)static
(2)private
package day10;
/*
内部类常用的修饰符:
static
private
*/
//class Outer2{
// static int a1 = 10;
// private static int a2 = 11;
// public static int a3 = 12;
//
// static class Inner1{
// public void fun1(){
// System.out.println(a1);
// System.out.println(a2);
// System.out.println(a3);
// }
// }
//}
class Outer3 {
private class Inner {
public void fun1(){
System.out.println("好好学习,天天向上!");
}
}
public void show(){
Inner inner = new Inner();
inner.fun1();
}
}
public class InnerDemo2 {
public static void main(String[] args) {
// Outer2.Inner1 inner1 = new Outer2.Inner1();
// inner1.fun1();
Outer3 outer3 = new Outer3();
outer3.show();
}
}
package day10;
class Outer {
public int num = 10;
class Inner {
public int num = 20;
public void show() {
int num = 30;
System.out.println(num); // 30
System.out.println(this.num); // 20
System.out.println(Outer.this.num); // 10
}
}
}
public class InnerDemo3 {
public static void main(String[] args) {
Outer.Inner inner = new Outer().new Inner();
inner.show();
}
}
package day10;
class Outer4 {
//成员方法
public void fun1() {
//局部变量
int a = 10; // 如果局部内部类中要使用方法中的局部变量的话,这个变量会被使用final关键字进行修饰
//局部内部类
class Inner {
int b = 20;
public void show() {
// a = 11; // 从内部类引用的本地变量必须是最终变量或实际上的最终变量
b = 12;
System.out.println(a);
System.out.println(b);
}
}
Inner inner = new Inner();
inner.show();
}
}
public class InnerDemo4 {
public static void main(String[] args) {
Outer4 outer4 = new Outer4();
outer4.fun1();
}
}
12、匿名内部类
1、格式
匿名内部类:
语法定义格式:
new 抽象类/接口(){
//要重写的方法
}
package com.shujia.day10.bao8;
/*
匿名内部类:
语法定义格式:
new 抽象类/接口(){
//要重写的方法
}
*/
abstract class Demo1{
public abstract void fun1();
// public abstract void fun2();
}
//class XXX extends Demo1{
// @Override
// public void fun1() {
// System.out.println("hello world");
// }
//}
//
//class Demo1Zi2 extends Demo1{
// @Override
// public void fun1() {
// System.out.println("好好学习,天天向上!");
// }
//}
class Student1{
public void show1(Demo1 demo1){
demo1.fun1();
}
}
public class NiMingClassDemo1 {
public static void main(String[] args) {
Student1 student1 = new Student1();
// student1.show1(new Demo1Zi());
// student1.show1(new Demo1Zi2());
/**
* 相当于jvm给我们做了几件事情
* 1、jvm底层自己造了一个类继承抽象类或者实现接口,这个类没有名字
* 2、重写的方法(实现的方法),我们只需要关注重写的方法
* 3、将这个类的对象造出来
*/
student1.show1(new Demo1() {
@Override
public void fun1() {
System.out.println("hello world");
}
});
student1.show1(new Demo1() {
@Override
public void fun1() {
System.out.println("好好学习,天天向上");
}
});
}
}
2、接口类型的方法调用,使用匿名内部类
package day10;
/*
接口类型的方法调用,使用匿名内部类
匿名内部类:
语法定义格式:
new 抽象类/接口(){
//要重写的方法
}
*/
interface Inter1{
void fun1();
}
//class Inter1Impl implements Inter1{
// @Override
// public void fun1() {
// System.out.println("hello java");
// }
//}
class Student2{
public void show(Inter1 inter1){
inter1.fun1();
}
}
public class NiMingClassDemo2 {
public static void main(String[] args) {
Student2 student2 = new Student2();
// student2.show(new Inter1Impl());
student2.show(new Inter1(){
@Override
public void fun1(){
System.out.println("hello 匿名类");
}
});
student2.show(new Inter1(){
@Override
public void fun1(){
System.out.println("hello java");
}
});
// student2.show(()-> System.out.println("hello world")); // lambda表达式
}
}
13、向上转型和向下转型
1、向上转型
好处:隐藏了子类型,提高了代码的扩展性。
坏处:只能使用父类的功能,不能使用子类特有功能,功能被限定。
使用场景:不需要面对子类型,通过提高扩展性,或者使用父类的功能即可完成操作,就是使用向上转型。
2、向下转型
好处:可以使用子类型的特有功能
坏处:面对具体的子类型,向下转型具有风险。即容易发生ClassCastException,只要转换类型和对象不匹配就会发生。解决方法:使用关键字instanceof。
14、浅拷贝和深拷贝
1、二者的区别
浅拷贝:在拷贝一个对象时,对对象的基本数据类型的成员变量进行拷贝,但对引用类型的成员变量只进行引用的传递,并没有创建一个新的对象,当对引用类型的内容修改会影响被拷贝的对象。
深拷贝:在拷贝一个对象时,除了对基本数据类型的成员变量进行拷贝,对引用类型的成员变量进行拷贝时,创建一个新的对象来保存引用类型的成员变量。
2、浅拷贝和深拷贝的应用
浅拷贝
java中clone方法是一个浅拷贝,引用类型依然在传递引用
深拷贝
实现深拷贝有两种方法:
(1) 序列化该对象,然后反序列化回来,就能得到一个新的对象了。
序列化:将对象写入到IO流中; 反序列化:从IO流中恢复对象 序列化机制允许将实现序列化的java对象转化为字节序列,这些字节序列可以保存到磁盘或者网络传输上,以达到以后恢复成原来的对象,序列化机制使得对象可以脱离程序的运行而独立存在。
(2) 继续利用clone()方法,对该对象的引用类型变量再实现一次clone()方法。
15、练习
-- 1、假如我们在开发一个系统时需要对员工类进行设计,员工包含3个属性:姓名、工号以及工资。
经理也是员工,除了含有员工的属性外,另为还有一个奖金属性。
请使用继承的思想设计出员工类和经理类。要求类中提供必要的方法进行属性访问。
-- 2、
编写程序实现比萨制作。需求说明编写程序,接收用户输入的信息,选择需要制作 的比萨。可供选择的比萨有:培根比萨和海鲜比萨。
实现思路及关键代码
1) 分析培根比萨和海鲜比萨
2) 定义比萨类
3) 属性:名称、价格、大小
4) 方法:展示
5) 定义培根比萨和海鲜比萨继承自比萨类
6) 定义测试类,根据输入信息产生具体的比萨对象
程序运行结果如图所示
-- 3、
编写程序实现软料购买:编写程序,接收用户输入的信息,选择购买的饮料。可供 选择的饮料有:咖啡、矿泉水和可乐。其中,购买咖啡时可以选择:加糖、加奶还
是什么都不加。购买可乐时可以选择:买可口可乐还是百事可乐。 程序运行效果如图所示。
16、答案
-- 1、
/*
分析:
父类:员工类(抽象类)
子类1:普通员工类 extends 员工类
子类2:经理类 extends 员工类
*/
package day09;
abstract class Staff{
private String name;
private String id;
private int salary;
public Staff() {
}
public Staff(String name, String id, int salary) {
this.name = name;
this.id = id;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
public abstract void work();
}
class Employee extends Staff{
public Employee() {
}
public Employee(String name, String id, int salary) {
// this.name = name;
// this.id = id;
// this.salary = salary;
super(name, id, salary);
}
@Override
public void work() {
System.out.println("牛马苦搬砖");
}
}
class Manager extends Staff{
private int bonus;
public Manager() {
}
public Manager(String name, String id, int salary, int bonus) {
super(name, id, salary);
this.bonus = bonus;
}
public int getBonus() {
return bonus;
}
public void setBonus(int bonus) {
this.bonus = bonus;
}
@Override
public void work() {
System.out.println("老板笑嘻嘻");
}
}
public class AbstractDemo2 {
public static void main(String[] args) {
Staff e1 = new Employee("张三", "gs1007", 1000); //抽象多态
e1.work();
//创建一个经理对象
Staff m1 = new Manager("李四", "gs1001", 1000000, 1000000000);
m1.work();
}
}
-- 2、
package day10;
import com.sun.org.apache.xpath.internal.objects.XString;
import java.util.Scanner;
class Pizza{
private String name;
private double price;
private int size;
public Pizza() {
}
public Pizza(String name, double price, int size) {
this.name = name;
this.price = price;
this.size = size;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public void show(){
};
}
class Bacon_Pizza extends Pizza{
private int bacon;
public Bacon_Pizza() {
}
public Bacon_Pizza(String name, double price, int size, int bacon) {
super(name, price, size);
this.bacon = bacon;
}
public int getBacon() {
return bacon;
}
public void setBacon(int bacon) {
this.bacon = bacon;
}
@Override
public void show(){
System.out.println("名称:"+getName());
System.out.println("价格:"+getPrice()+"元");
System.out.println("大小:"+getSize()+"寸");
System.out.println("培根克数:"+getBacon());
}
}
class Seafood_pizza extends Pizza{
private String Seafood;
public Seafood_pizza() {
}
public Seafood_pizza(String name, double price, int size, String seafood) {
super(name, price, size);
Seafood = seafood;
}
public String getSeafood() {
return Seafood;
}
public void setSeafood(String seafood) {
Seafood = seafood;
}
@Override
public void show(){
System.out.println("名称:"+getName());
System.out.println("价格:"+getPrice()+"元");
System.out.println("大小:"+getSize()+"寸");
System.out.println("配料:"+getSeafood());
}
}
public class HomeWork01 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while(1==1){
System.out.println("请选择想要制作的比萨(1.培根披萨 2.海鲜披萨 3.退出):");
int num = sc.nextInt();
if(num==1){
Bacon_Pizza b = new Bacon_Pizza();
b.setName("培根披萨");
System.out.println("请输入培根克数:");
b.setBacon(sc.nextInt());
System.out.print("请输入披萨大小:");
b.setSize(sc.nextInt());
System.out.print("请输入披萨价格:");
b.setPrice(sc.nextDouble());
b.show();
} else if (num==2) {
Seafood_pizza s = new Seafood_pizza();
s.setName("海鲜披萨");
System.out.println("请输入配料信息:");
s.setSeafood(sc.next());
System.out.print("请输入披萨大小:");
s.setSize(sc.nextInt());
System.out.print("请输入披萨价格:");
s.setPrice(sc.nextDouble());
s.show();
}else if(num==3){
break;
}
}
}
}
-- 3、
package day10;
import java.util.Scanner;
class Drink{
private String name;
private int volume;
public Drink() {
}
public Drink(String name, int volume) {
this.name = name;
this.volume = volume;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getVolume() {
return volume;
}
public void setVolume(int volume) {
this.volume = volume;
}
public void show(){
}
}
class Coffee extends Drink{
@Override
public void show(){
Scanner sc = new Scanner(System.in);
System.out.print("请输入购买容量:");
super.setVolume(sc.nextInt());
System.out.print("请问是否要配料:(1、加糖 2、加奶 3、什么都不加):");
int num = sc.nextInt();
System.out.println("您购买饮料信息如下。");
System.out.println("名称:咖啡");
if(num == 1){
System.out.println("添加配料:加糖");
} else if (num == 2) {
System.out.println("添加配料:加奶");
}else{
System.out.println("添加配料:无");
}
System.out.println("容量:"+getVolume());
}
}
class Water extends Drink{
@Override
public void show() {
Scanner sc = new Scanner(System.in);
System.out.print("请输入购买容量:");
super.setVolume(sc.nextInt());
System.out.println("您购买饮料信息如下。");
System.out.println("名称:矿泉水");
System.out.println("容量:"+getVolume());
}
}
class Cola extends Drink{
@Override
public void show() {
Scanner sc = new Scanner(System.in);
System.out.print("请输入购买容量:");
super.setVolume(sc.nextInt());
System.out.print("请问需要哪一款可乐:(1.可口可乐 2.百事可乐):");
int num = sc.nextInt();
if(num == 1){
System.out.println("名称:可口可乐");
} else if (num == 2) {
System.out.println("名称:百事可乐");
}
System.out.println("容量:"+getVolume());
}
}
public class HomeWork02 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("请选择饮料(1、咖啡 2、矿泉水 3、可乐):");
int num = sc.nextInt();
if(num == 1){
Coffee c = new Coffee();
c.show();
} else if (num == 2) {
Water w = new Water();
w.show();
} else if (num == 3) {
Cola cl = new Cola();
cl.show();
}
}
}