多态重点
面向对象的三大特征:封装、继承、多态
多态的形式:
父类类型 变量名 = new 子类/实现类构造器;
变量名.方法名();
public class PolymorphicDemo {
public static void main(String[] args) {
// 父类类型 对象名称 = new 子类构造器;
Animal dlam = new Cat();
dlam.run(); // 对于方法的调用:编译看左边,运行看右边。
System.out.println(dlam.name); // 对于变量的调用:编译看左边,运行看左边。
Animal taiDi = new Dog();
taiDi.run(); // 对于方法的调用:编译看左边,运行看右边。
System.out.println(taiDi.name); // 对于变量的调用:编译看左边,运行看左边。
}
}
class Dog extends Animal{
public String name = "🐶名称Dog";
@Override
public void run(){
System.out.println("🐕跑的贼快~~~~!");
}
}
class Cat extends Animal{
public String name = "🐱名称Cat";
@Override
public void run(){
System.out.println("🐱跑的飞快~~~~!");
}
}
class Animal{
public String name = "动物名称Animal";
public void run(){
System.out.println("动物跑!");
}
}
**多态的概念:**同一个类型的对象,执行同一个行为,在不同的状态下会表现出不同的行为特征
多态的识别技巧:
- 对于方法的调用:编译看左边,运行看右边
- 对于变量的调用:编译看左边,运行看左边
多态的使用前提
- 必须存在继承或者实现关系
- 必须存在父类类型的变量引用子类类型的对象
- 需要存在方法重写
多态的优劣势
优势:
- 在多态形式下,右边对象可以实现组件化切换,业务功能也随之改变,便于扩展和维护。可以实现类与类之间的解耦
- 实际开发中,父类类型作为方法形式参数,传递之类对象给方法,可以传递一切子类对象进行方法的调用,更能体现出多态的扩展性与便利
劣势:
- 多态形式下,不能直接调用子类特有的功能。编译看左边!!左边父类没有子类独有的功能,所以代码在编译阶段就直接报错了
引用类型自动类型转换
基本数据类型的转换
- 小范围类型的变量或者值可以直接赋值给大范围类型的变量。
- 大范围类型的变量或者值必须强制类型转换给小范围类型的变量。
所以引用类型转换的思想也一样
子类类型的对象或者变量可以自动类型转换赋值给父类类型的变量
引用类型的自动类型转换并不能解决多态的劣势
引用类型的强制类型转换
父类类型的变量或者对象必须强制类型转换成子类类型的变量,否则报错!
类型 变量名称 = (类型)(对象或者变量)
注意:有继承/实现关系的两个类型就可以进行强制类型转换,编译阶段一定不报错!但是运行阶段可能出现:类型转换异常 ClassCastException
Java建议在进行强制类型转换之前先判断变量的真实类型,再强制类型转换!
变量 instanceof 类型
: 判断前面的变量是否是后面的类型或者其子类类型才会返回true
public class PolymorphicDemo {
public static void main(String[] args) {
Animal a = new Wolf();
a.run();
// a.catchSheep(); // 报错!
// 1.把动物类型的变量a 转换成 真实的狼类型
Wolf w = (Wolf) a;
w.catchSheep();
// 2.多态下类型转换异常问题研究(重点)
Animal a1 = new Cat();
//Wolf w1 = (Wolf) a1; // 编译阶段没有报错!在运行阶段出现ClassCastException类型转换成!
if(a1 instanceof Cat){
Cat c1 = (Cat) a1;
c1.catchMouse();
}else if(a1 instanceof Wolf){
Wolf w1 = (Wolf) a1;
w1.catchSheep();
}
}
}
class Wolf extends Animal{
@Override
public void run(){
System.out.println("狼跑的飞快~~~");
}
public void catchSheep(){
System.out.println("🐺抓🐏");
}
}
class Cat extends Animal{
@Override
public void run(){
System.out.println("猫跑的贼快~~~");
}
public void catchMouse(){
System.out.println("🐱抓🐀~~");
}
}
class Animal{
public void run(){
System.out.println("动物可以跑~~");
}
}
多态接口的综合案例
public class Demo {
public static void main(String[] args) {
// 1.买一部电脑
Computer c = new Computer();
// 2.买一个鼠标
USB xiaoMi = new Mouse("小米鼠标");
c.install(xiaoMi);
// 3.买一个键盘
KeyBoard sfy = new KeyBoard("双飞燕键盘");
c.install(sfy);
}
}
class Computer{
// 提供一个安装USB设备的入口
public void install(USB usb){
usb.connect();
// usb.dbclick();
// usb可能是鼠标,也可能键盘 .....
if(usb instanceof Mouse){
Mouse m = (Mouse) usb;
m.dbclick();
}else if(usb instanceof KeyBoard){
KeyBoard k = (KeyBoard) usb;
k.keyDown();
}
usb.unconnect();
}
}
// 定义2个USB设备:鼠标,键盘。
class Mouse implements USB{
private String name;
public Mouse(String name) {
this.name = name;
}
// 双击
public void dbclick(){
System.out.println(name+"双击了,老铁,6666666~~~~");
}
@Override
public void connect() {
System.out.println(name+"成功接入了设备~~~~");
}
@Override
public void unconnect() {
System.out.println(name+"成功拔出了设备~~~~");
}
}
class KeyBoard implements USB{
private String name;
public KeyBoard(String name) {
this.name = name;
}
// 按键
public void keyDown(){
System.out.println(name+"写下了,来了,老弟~~记得点亮小💗💗....");
}
@Override
public void connect() {
System.out.println(name+"成功接入了设备~~~~");
}
@Override
public void unconnect() {
System.out.println(name+"成功拔出了设备~~~~");
}
}
// 定义USB的规范,必须要完成接入和拔出的功能!!
interface USB{
void connect(); // 接入
void unconnect(); // 拔出
}
内部类
内部类是类的五大成分之一:成员变量、方法、构造器、代码块、内部类
内部类是定义在一个类里面的类
内部类有什么用
- 可以提供更好的封装性
- 内部类有更多的权限修饰符
- 其封装有更多的控制
- 可以体现出组件的思想
内部类的分类:
- 静态内部类
- 实例内部类(成员内部类)
- 局部内部类
- 匿名内部类
静态内部类
有static
修饰,属于外部类本身,会加载一次
成分研究:
- 类有的成分它都有,静态内部类属于外部类本身,只会加载一次
- 所以它的特点与外部类是完全一样的,只是位置在别人里面而已。
外部类=宿主
内部类=寄生
静态内部类的访问格式:
外部类名称.内部类名称
静态内部类创建对象的格式:
外部类名称.内部类名称 对象名称 = new 外部类名称.内部类构造器;
静态内部类的访问拓展:
- 静态内部类中是否可以直接访问外部类的静态成员?可以的,外部类的静态成员只有一份,可以被共享!
- 静态内部类中是否可以直接访问外部类的实例成员?不可以的,外部类的是成员必须用外部类对象访问!!
class Outter{
public static int age1 = 12;
private double salary;
// 静态内部类:有static修饰,属于外部类本身,只会加载一次
public static class Inner{
private String name;
private int age;
public static String schoolName = "黑马";
public void show() {
System.out.println(name+"-->"+age+"岁~");
System.out.println(age1);
//System.out.println(salary);
}
public Inner() {
}
public Inner(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;
}
}
}
实例内部类(成员内部类)
无static
修饰的内部类,属于外部类的每个对象的,跟着对象一起加载的
实例内部类的成分特点:
- 实例内部类中不能定义静态成员,其他都可以定义
- 可以定义常量
实例内部类的访问格式:
外部类名称.内部类名称
创建对象的格式:
外部类名称.内部类名称 对象名称 = new 外部类构造器.new 内部构造器;
拓展:
-
实例内部类中是否可以直接访问外部类的静态成员
可以的,外部类的静态成员可以被共享访问
-
实例内部类中是否可以访问外部类的实例成员
可以的,实例内部类属于外部类对象,可以直接访问当前外部类对象的实例成员
实例内部类属于外部类对象,需要用外部类对象一起加载,实例内部类可以访问外部类的全部成员
public class InnerClass {
public static void main(String[] args) {
// 实例内部类属于外部类对象。实例内部类的宿主是外部类对象!!
Outter.Inner in = new Outter().new Inner();
in.show();
}
}
// 外部类
class Outter{
public static int age = 1;
private double salary;
// 实例内部类:无static修饰,属于外部类的对象
public class Inner{
private String name ;
public static final String schoolName = "黑马";
// 不能在实例内部类中定义静态成员!!!
// public static String schoolName = "黑马";
// public static void test(){
//
// }
// 实例方法
public void show(){
System.out.println(name+"名称!");
System.out.println(age);
System.out.println(salary);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
局部内部类
定义在方法中,在构造器中,代码块中,for循环中定义的内部类,就是局部内部类。
局部内部类中的成分特点:
- 只能定义实例成员,不能定义静态成员
- 可以定义常量的
public class InnerClass {
static {
abstract class A{
}
}
public static void main(String[] args) {
class A{
private String name;
public void test(){
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
A a = new A();
a.test();
}
public static void test(){
class Animal{
}
class Cat extends Animal{
}
}
}
匿名内部类
就是一个没有名字的局部内部类
匿名内部类目的是为了:简化代码,也是开发中常用的形式
匿名内部类的格式:
new 类名|抽象类|接口(形参){
方法重写。
}
匿名内部类的特点:
- 匿名内部类是一个没有名字的内部类
- 匿名内部类一旦写出来,就会立即创建一个匿名内部类的对象返回
- 匿名内部类的对象的类型相当于是当前
new
的那个的类型的子类类型。
public class Anonymity {
public static void main(String[] args) {
Animal a = new Animal(){
@Override
public void run() {
System.out.println("猫跑的贼溜~~");
}
};
a.run();
a.go();
Animal a1 = new Animal() {
@Override
public void run() {
System.out.println("狗跑的贼快~~~");
}
};
a1.run();
a.go();
}
}
abstract class Animal{
public abstract void run();
public void go(){
System.out.println("开始go~~~");
}
}
匿名内部类的使用形式
public class Anonymity02 {
public static void main(String[] args) {
Swim bozai = new Swim() {
@Override
public void swimming() {
System.out.println("老师🏊的贼溜~~~~");
}
};
go(bozai);
Swim boniu = new Swim() {
@Override
public void swimming() {
System.out.println("波妞学生快乐的狗爬式~~~");
}
};
go(boniu);
go(new Swim() {
@Override
public void swimming() {
System.out.println("波妞2学生快乐的狗爬式~~~");
}
});
}
// 提供一个方法让全部角色进入比赛
public static void go(Swim s){
System.out.println("开始。。。。");
s.swimming();
System.out.println("结束。。。。");
}
}
interface Swim{
void swimming();
}
包和权限修饰符
包
- 分门别类的管理各种不同的技术。
- 企业的代码必须用包区分。便于管理技术,扩展技术,阅读技术。
定义包的格式:
package 包名;
必须放在类名的最上面
一般工具已经帮我们做好了
包名的命名规范:
- 一般是公司域名的倒写+技术名称:
- http://www.itheima.com => com.itheima.技术名称
- 包名建议全部用英文,多个单词用”.“连接,必须是合法标识符,不能用关键字
注意
- 相同包下的类可以直接访问
- 不同包下的类必须导包,才可以使用
- 导包格式:
import 包名.类名;
权限修饰符
权限修饰符:有四种(private
-> default
-> protected
- > public
)
可以修饰成员变量,修饰方法,修饰构造器,内部类,不同修饰符修饰的成员能够被访问的权限将受到限制!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xjOSwnVZ-1654911509441)(imgs/image-20220525204748483.png)]
Object类
Object
类是Java中的祖宗类
- 一个类要么默认继承了
Object
类,要么间接继承了Object
类 Object
类的方法是一切子类都可以直接使用的,所以我们要学习Object
类的方法。
Object
类的常用方法:
-
public String toString():
-
默认是返回当前对象在堆内存中的地址信息
com.itheima._12Object类的详细使用.Student@735b478
-
默认的地址信息格式:类的全限名@内存地址
-
直接输出对象名称,默认会调用toString()方法,所以直接输出对象可以省略toString()不写。
-
实际开发中直接输出对象,输出对象的地址其实是没有意义的。
-
所以
toString
方法存在的意义是为了被子类重写,以便能够返回对象的数据内容输出。因为实际开发中我们输出对象更多的时候是希望看到对象的数据内容信息
-
- 开发中如果希望输出对象看到对象的内容,只需要重写
toString()
方法即可- 所以
toString
方法存在的意义是为了被子类重写
public boolean equals(Object o)
- 默认是比较两个对象的地址是否相同。相同返回
true
,反之 - 直接比较两个对象的地址是否相同完全可以用
==
替代equals
,所以equals
存在的意义是为了被子类重写,以便程序员可以自己来定制比较规则
- 默认是比较两个对象的地址是否相同。相同返回
只要两个对象的内容一样,我们就认为他们是相等的。
equals
存在的意义是为了被子类重写,以便程序员自己来定制比较规则
@Override
public boolean equals(Object o) {
// 1.判断是否自己和自己比较,如果是同一个对象比较直接返回true
if (this == o) return true;
// 2.判断被比较者是否为null ,以及是否是学生类型。
if (o == null || this.getClass() != o.getClass()) return false;
// 3.o一定是学生类型,强制转换成学生,开始比较内容!
Student student = (Student) o;
return age == student.age &&
sex == student.sex &&
Objects.equals(name, student.name);
}
// 重写Object的toString()以便返回对象的内容数据
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
'}';
}
idea可以自动生成
equals()
和toString()
,直接按下快捷键ALT+INSERT
,选择generate
即可
Objects类
Objects
类与Object
还是继承关系Objects
类是从JDK 1.7开始之后才有的。
Objects
的方法:
-
public static boolean equals(Object a, Object b)
-
比较两个对象的
-
底层进行非空判断,从而可以避免空指针异常。更安全,推荐使用
public static boolean equals(Object a, Object b) { return a == b || a != null && a.equals(b); }
-
public static boolean isNull(Object obj)
判断变量是否为
null
,为null
返回true
, 反之false
-
Date类
Java是面向对象的,会用一个类代表一个事物
Date
类在Java中代表的是系统当前此刻日期时间对象。
Date
类:
包:java.util.Date
- 构造器:
public Date()
创建当前系统的此刻日期时间对象。
public Date(long time)
import java.util.Date;
public class DateDemo01 {
public static void main(String[] args) {
// a.创建一个日期对象代表了系统此刻日期时间对象
Date d = new Date();
System.out.println(d);
// b.拿当前日期对象的时间毫秒值
long time = d.getTime();
System.out.println(time);
}
}
-
方法
public long getTime()
: 返回自 1970 年 1 月 1 日 00:00:00 GMT 以来走过的总的毫秒数。
时间记录的两种方式:
Date
日期对象。
时间毫秒值:从1970-01-01 00:00:00开始走到此刻的总的毫秒值。 1s = 1000ms
小结:
Date
可以代表系统当前此刻日期时间对象。- 时间记录的两种方式:
Date
日期对象。- 时间毫秒值:从1970-01-01 00:00:00开始走到此刻的总的毫秒值。 1s = 1000ms
import java.util.Date;
public class DateDemo02 {
public static void main(String[] args) {
// 1.拿到此刻日期时间对象的毫秒值
long startTime = new Date().getTime();
for(int i = 1; i < 1000000 ; i++ ){
System.out.println("输出:"+i);
}
// 2.拿到此刻日期时间对象的毫秒值
long endTime = new Date().getTime();
System.out.println( (endTime - startTime) / 1000.0 +"s");
}
}
Date
类的有参数构造器的使用。
构造器:
public Date()
:创建当前系统的此刻日期时间对象。public Date(long time)
:把时间毫秒值转换成日期对象。
流程
Date
日期对象 ->getTime()
-> 时间毫秒值- 时间毫秒值 ->
new Date(时间毫秒值)
->Date
日期对象