JavaSE
多态
概述
定义
- 多态:是指同一行为,对于不同的对象具有多个不同表现形式。
- 程序中多态:是指同一方法,对于不同的对象具有不同的实现。
前提条件
- 继承或者实现(决定性条件)
1.继承:类和类之间的多态
2.实现:类和接口之间的多态 - 父类引用指向子类对象(格式体现)
1.父类型变量接收子类型对象
2.父类型形参接收子类型实参对象
3.父类型返回值类型接收子类型对象 - 方法的重写(意义体现)
实现多态
多态的体现:父类的引用指向它的子类的对象
父类类型 变量名 = new 子类对象;
变量名.方法名();
tips:父类类型:指子类对象继承的父类类型,或者实现的父接口类型。
class Animal{}
class Cat extends Animal{}
class Dog extends Animal{}
clsss Person{}
//测试类:
main(){
Animal a1 = new Cat();
Animal a2 = new Dog();
Animal a3 = new Person();//编译错误,没有继承关系。
}
多态时访问成员的特点
- 多态时成员变量的访问特点(编译看左边,运行看左边)
1.父类型变量只能访问父类型变量,不能访问子类型成员变量
2.子类型变量可以访问自己成员变量及父类的成员变量
Animal a = new Cat();
// a只能访问Animal中的成员变量,编译和运行都看左边
- 多态时成员放法的访问特点
1.编译的时候去父类中查找方法,运行的时候去子类中查找方法来执行(非静态方法:编译看左边,运行看右边)
2.编译的时候去父类中查找方法,运行的时候去父类中查找方法来执行(静态方法:编译看左边,运行看左边) - 注意:多态的情况下是无法访问子类独有的方法
多态的几种表现形式
- 普通父类多态
public class Fu{}
public class Zi extends Fu{}
public class Demo{
public static void main(String[] args){
Fu f = new Zi();//左边是一个“父类”
}
}
- 抽象父类多态
public abstract class Fu{}
public class Zi extends Fu{}
public class Demo{
public static void main(String[] args){
Fu f = new Zi();//左边是一个“父类”
}
}
- 父接口多态
public interface A{}
public class AImp implements A{}
public class Demo{
public static void main(String[] args){
A a = new AImp();
}
}
多态的应用场景
public class Demo1 {
public static void main(String[] args) {
/*
多态的应用场景:
1.变量多态
2.形参多态
3.返回值多态
*/
// 1.变量多态
Animal anl = new Dog();
anl.eat();
// 2.形参多态
Dog dog = new Dog();
invokeEat(dog);
Cat cat = new Cat();
invokeEat(cat);// 实参赋值给形参: Animal anl = new Cat();
// 3.返回值多态
Animal anl2 = getAnimal();// 返回值赋值给变量: Animal anl2 = new Dog()
}
// 3.返回值多态
// 结论:如果方法的返回值类型为父类类型,那么就可以返回该父类对象以及其所有子类对象
public static Animal getAnimal(){
// return new Animal();
return new Dog();
// return new Cat();
}
// 形参多态: 当你调用invokeEat方法的时候,传入Animal类的子类对象
// Animal anl = dog; ====> Animal anl = new Dog();
// 结论:如果方法的参数是父类类型,那么就可以接收所有该父类对象以及其所有子类对象
// Object类:是java中所有类的父类,所以如果参数为Object类型,那么就可以传入一切类的对象
public static void invokeEat(Animal anl){
anl.eat();
}
}
多态的好处和弊端
- 好处
提高了代码的扩展性、灵活性、尤其体现在方法中的参数和返回值 - 弊端
多态的情况下,只能调用父类的共性内容,不能调用子类的特有内容 - 示例
// 父类
public abstract class Animal {
public abstract void eat();
}
// 子类
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
}
引用类型转换
向上转型
子类类型向父类类型向上转换的过程,这个过程是默认的。
Aniaml anl = new Cat();
向下转型
父类类型向子类类型向下转换的过程,这个过程是强制的。
Aniaml anl = new Cat();
Cat c = (Cat)anl;//向下转型
c.catchMouse();// 可以访问 子类独有的功能,解决多态的弊端
instance of关键字
- 向下强转有风险,最好在转换前做一个验证
- 格式
变量名 instanceof 数据类型
如果变量属于该数据类型,返回true。
如果变量不属于该数据类型,返回false。
if( anl instanceof Cat){//判断anl是否能转换为Cat类型,如果可以返回:true,否则返回:false
Cat c = (Cat)anl;//安全转换
}
内部类
内部类的概述
什么是内部类
将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。
成员内部类
- 概述:定义在类中方法外的类。
- 定义格式
class 外部类 {
class 内部类{
}
}
在描述事物时,若一个事物内部还包含其他事物,就可以使用内部类这种结构。比如,汽车类Car
中包含发动机类Engine
,这时,Engine
就可以使用内部类来描述,定义在成员位置。
- 示例
class Car { //外部类
class Engine { //内部类
}
}
访问特点
- 内部类可以直接访问外部类的成员,包括私有成员。
- 外部类要访问内部类的成员,必须要建立内部类的对象。
- 创建内部类对象格式
外部类名.内部类名 对象名 = new 外部类型().new 内部类型();
- 示例
public class Body {// 外部类
// 成员变量
private int numW = 10;
int num = 100;
// 成员方法
public void methodW1(){
System.out.println("外部类中的methodW1方法...");
}
public void methodW2(){
System.out.println("外部类中的methodW2方法...");
// 创建内部类对象
Body.Heart bh = new Body().new Heart();
// 访问内部类成员变量
System.out.println("内部类成员变量numN:"+bh.numN);
// 访问内部类成员方法
bh.methodN2();
}
public class Heart{// 成员内部类
// 成员变量
int numN = 20;
int num = 200;
// 成员方法
public void methodN(){
System.out.println("内部类中的methodN方法...");
// 访问外部类成员变量
System.out.println("外部类成员变量:"+numW);
// 访问外部类成员方法
methodW1();
}
public void methodN2(){
System.out.println("内部类中的methodN2方法...");
}
public void methodN3(){
int num = 300;
System.out.println("局部变量num:"+num);// 300
System.out.println("内部类成员变量num:"+this.num);// 200
System.out.println("外部类成员变量num:"+Body.this.num);// 100
}
}
}
public class Demo {
public static void main(String[] args) {
// 测试
// 创建外部类对象,调用外部类的方法methodW2
Body body = new Body();
body.methodW2();
System.out.println("=======================");
// 创建内部类对象,调用内部类的methodN方法
Body.Heart heart = new Body().new Heart();
heart.methodN();
System.out.println("=======================");
heart.methodN3();// 300 200 100
}
}
匿名内部类
- 概述
是内部类的简化写法。它的本质是一个带具体实现的
父类或者父接口的
匿名的
子类对象。 - 示例一
public abstract class Animal {
public abstract void eat();
}
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃骨头...");
}
}
public class Test {
public static void main(String[] args) {
/*
- 匿名内部类的概述:本质就是继承了父类或者实现了接口的匿名子类的对象
- 匿名内部类的格式:
new 类名\接口名(){
方法重写
};
- 匿名内部类的作用: 为了简化代码,并没有什么特殊的功能
需求: 调用Aniaml类的eat()方法
1.创建一个子类继承Animal类
2.在子类中重写eat方法
3.创建子类对象
4.使用子类对象调用eat方法
想要调用抽象类中的方法,必须具备以上4步,那能不能减后呢? 可以 使用匿名内部类
*/
Animal anl1 = new Dog();
anl1.eat();
Animal anl2 = new Animal() {
@Override
public void eat() {
System.out.println("Animal子类的eat方法...");
}
};
anl2.eat();
}
}
- 示例二
public interface AInterface {
void method();
}
public class AImp implements AInterface {
@Override
public void method() {
System.out.println("AImp 实现类重写method方法....");
}
}
public class Demo {
public static void main(String[] args) {
/*
匿名内部类:
本质是一个继承了父类的匿名子类的对象
本质是一个实现了接口的匿名实现类的对象
案例: A接口中有一个抽象方法method(),现在需要调用A接口中的method方法
思路:
1.创建一个实现类实现A接口
2.重写A接口中的抽象方法method()
3.创建实现类对象
4.使用实现类对象调用method方法
想要调用A接口中的method方法,按照传统方式,必须有以上4步,一步都不可少
前面三步就是为了得到A接口的实现类对象
现在: 匿名内部类可以表示一个接口的匿名实现类对象,所以,可以直接创建接口的匿名内部类来调用method方法即可
*/
AInterface a = new AInterface(){
@Override
public void method() {
System.out.println("匿名内部类方式重写method方法....");
}
};
a.method();
System.out.println("==================");
AInterface a2 = new AImp();
a2.method();
System.out.println("======================");
AInterface a3 = new AInterface() {
@Override
public void method() {
// 实现
}
};
}
}
权限修饰符
- 概述
在Java中提供了四种访问权限,使用不同的访问权限修饰符修饰时,被修饰的内容会有不同的访问权限,public:公共的;protected:受保护的;(空的):默认的;private:私有的 - 不同权限的访问能力
public | protected | (空的) | private | |
---|---|---|---|---|
同一类中 | √ | √ | √ | √ |
同一包中(子类与无关类) | √ | √ | √ | |
不同包的子类 | √ | √ | ||
不同包中的无关类 | √ |
- 大小关系
public > protected > (空的) > private - 编写代码时,如果没有特殊的考虑,建议这样使用权限
1.成员变量使用private,隐藏细节。
构造方法使用public,方便创建对象。
成员方法使用public,方便调用方法。
代码块
构造代码块
格式: {}
位置: 类中,方法外
执行: 每次在调用构造方法的时候,就会执行
使用场景: 统计创建了多少个该类对象
例如:
public class Person{
{
构造代码块执行了
}
}
静态代码块
格式:static{}
位置: 类中,方法外
执行: 当类被加载的时候执行,并只执行一次
使用场景: 例如加载驱动,这种只需要执行一次的代码就可以放在静态代码块中
public class Person {
private String name;
private int age;
//静态代码块
static{
System.out.println("静态代码块执行了");
}
}
局部代码块
格式:{}
位置: 方法中
执行: 调用方法,执行到局部代码块的时候就执行
使用场景: 节省内存空间,没有多大的意义
例如:
public static void main(String[] args) {
int a = 10;
//局部代码块
//局部代码块的作用就是限制变量的作用域,早早的从内存中消失,节约内存
//但是现在内存不值钱,所以局部代码块没有使用意义
{
int b = 20;
}
//int a = 30; //在同一个区域不能定义重名变量
//不报错,以为作用域不同
int b = 40;
}