- 继承
- 重写Override
- 抽象方法
- 接口
- 默认方法
- 静态方法
- 私有方法
- 常量的使用
- 多态
- instanceof
- 权限修饰符
- final关键字
- 修饰一个类
- 修饰一个方法
- 修饰一个局部变量(Demo01Final)
- 修饰一个成员变量
- 内部类
- 成员内部类
- 局部内部类
- 匿名内部类
一、面向对象的三大特征:封装性 继承性 多态性
二、继承是多态的前提 没有继承就没有多态
三、继承主要解决的问题是共性抽取
四、继承可以理解为 师傅会多少武功 大徒弟也都会 小徒弟也都会 大徒弟小徒弟都继承了师傅的武功
五、我们把师傅称谓父类 基类 超类 两个徒弟称为子类 派生类
六、继承关系中的特点:
- 子类可以拥有父类的“内容”
- 子类还可以拥有自己专有的内容
七、在继承关系中 “子类就是一个父类” 也就是说 子类可以被当做父类看待,例如:父类是员工 子类是讲师 那么“讲师就是一个员工” 关系是:子类 is a 父类
八、定义父类的格式:(一个普通的类定义):
public class 父类名称{
//……
}
九、定义子类的格式:
public class 子类名称 extends 父类名称{
//……
}
十、一个父类都不知道自己有多少儿子 不知道自己儿子是谁 也不知道自己儿子在哪里 只有子类才知道自己的父亲是谁
十一、java语言是单继承的 一个类的直接父类只能有唯一一个 一个类只能继承一个类 不能继承多个类 一个儿子只能有一个亲生父亲
十二、java语言可以多级继承 我有一个父亲 我的父亲还有一个父亲 也就是爷爷
十三、object类没有父类
十四、一个子类的直接父亲是唯一的 但是一个父类可以拥有很多个子类 也就是说可以有很多个兄弟姐妹
十五、继承关系中 父子类构造方法的访问特点:
- 子类构造方法中有一个默认隐含的
super()
调用 所以一定是先调用父类构造 - 子类构造可以通过super关键字来调用父类重载构造
public class Fu2 {
public Fu2(int Number) {
System.out.println("父类构造方法!");
}
}
public Zi2() {
// 因为系统默认赠送的是没有参数的super();
// 如果父类是含有参数的构造方法 那么系统就会报错
// 此时需要自己手动添加一个含参的super
super(10);
System.out.println("子类构造方法!");
}
- super的父类构造调用 必须是子类构造方法的第一个语句 不能一个子类构造调用多次super构造 即 一个子类构造只能有一个super语句
public class Fu2 {
public Fu2() {
System.out.println("父类构造方法!");
}
public Fu2(int Number) {
System.out.println("父类构造方法! " + Number);
}
}
//若子类构造方法用super()
public class Zi2 extends Fu2{
public Zi2() {
System.out.println("子类构造方法!");
}
}
打印输出:
父类构造方法!
子类构造方法!
//若子类构造方法用super(10)
public class Zi2 extends Fu2{
public Zi2() {
super(10);
System.out.println("子类构造方法!");
}
打印输出:
父类构造方法!10
子类构造方法!
十六、【总结】:子类必须调用父类构造方法 不写则赠送super();
,写了则用写的指定的super调用 super只能有一个 还必须是第一个
十七、super关键字的用法有三种:
- 在子类的成员方法中 访问父类的成员变量
- 在子类的成员方法中 访问父类的成员方法(Zi2)
- 在子类的构造方法中 访问父类的构造方法(Zi2)
十八、this关键字的用法有三种:
- 在本类的成员方法中 访问本类的成员变量
- 在本类的成员方法中 访问本类的另一个成员方法(Zi2)
- 在本类的构造方法中 访问本类的另一个构造方法(Fu2)
十九、【注】:
this(……)
调用也必须是构造方法的第一个语句- super和this两种构造调用不能同时使用 在构造方法的开头第一个语句写了
this(……)
语句 那么默认的super()
将会消失
public class Constructor {
public static void main(String[] args) {
Zi2 zi2 = new Zi2();
/*new了个对象 肯定会调用构造方法 可是子类继承了父类 因为子类继承父类的super语句写在了子类构造方法的第一句
* 所以先执行父类的方法 再执行子类的方法 打印结果为:
* 父类构造方法!
* 子类构造方法!
*/
}
}
二十、在父子类的继承关系当中 如果成员变量重名 则创建子类对象时 访问有两种方式:
- 直接通过子类对象访问成员变量:(
子类对象.成员变量
)等号左边是谁 就优先用谁 没有则向上找 - 间接通过成员方法访问成员变量:该方法属于谁(该类定义在哪儿)就优先用谁 没有则向上找
二十一、在父子类的继承关系当中 如果局部变量 父类的成员变量 本类的成员变量重名 该怎么办: - 局部变量:直接写成员变量名
- 本类的成员变量名:
this.成员变量名
- 父类的成员变量:
super.成员变量名
二十二、在父子类的继承关系当中 如果父类的方法和子类的重名了 创建子类对象 访问成员方法的规则:创建的对象是谁 就优先用谁 如果没有则向上找
【注】:无论是成员方法还是成员变量 如果没有都是向上找父类 绝对不会向下找子类
import javax.sound.midi.Soundbank;
public class Inherit2 {
public static void main(String[] args) {
Fu fu = new Fu();
System.out.println(fu.numFu);//只能使用父类的东西 没有任何子类内容 10
Zi zi = new Zi();//等号左边是Zi 优先用子类的
System.out.println(zi.numFu);//10 优先用子类 子类没有 向上找 找到父类中有 用父类的
System.out.println(zi.numZi);//20
System.out.println("父子类成员变量重名:===================");
//直接通过子类对象访问成员变量:(子类对象.成员变量)等号左边是谁 就优先用谁 没有则向上找
System.out.println(zi.num);//优先子类 200
//System.out.println(zi.abc);//优先用子类 子类没有 向上找 发现父类也没有 编译报错
System.out.println("间接方法:======================================");
zi.methodZi();//这个方法是子类的 优先用子类的 没有再向上找 200
zi.methodFu();//这个方法是在父类当中定义的 100
System.out.println("局部变量 父子类成员变量重名:=====================================");
zi.method();
System.out.println("子类父类方法重名:====================================");
zi.name();//创建的是new了子类对象 所以优先用子类方法
}
}
重写Override
一、重写(Override):在继承关系中 方法的名称一样 参数列表也一样 也称覆盖 覆写
二、重写(Override)和重载(Overload)的区别:
- 重写(Override):在继承关系中 方法的名称一样 参数列表也一样
- 重写(Override):方法的名称一样 参数列表不一样
三、重写的特点:创建的是子类对象 就优先用子类方法 new的是谁就优先用谁
四、注意事项:
- 必须保证父子类之间方法的名称相同 参数列表也相同
@Override
:写在覆盖重写的方法前面 用来检测是不是有效的正确覆盖重写 可写可不写 建议写上- 子类方法的返回值必须小于等于父类方法的返回值范围
java.lang.Object
类是所有类的公共最高父类(祖宗类)java.lang.String
是Object的子类- 子类方法的权限修饰符必须大于等于父类方法的权限修饰符:
public > protected > (default) > private
,其中,(default)不是关键字default 而是什么都不写 留空
/*
一、抽象方法:如果父类当中的方法不确定如何进行{}方法体实现 那么这就是一个抽象方法
二、抽象方法:就是加上abstract关键字 然后去掉大括号 直接分号结束
三、抽象类:抽象方法所在的类 必须是抽象类才行 在class之前加上abstract即可 抽象类中可以含有普通的成员方法
四、如何使用抽象类和抽象对象:
- 不能直接创建抽象类对象 即 不能直接new抽象类对象
- 必须用一个子类来继承抽象父类
- 子类必须覆盖重写抽象父类中所有的抽象方法
覆盖重写Override(实现):子类去掉抽象方法的abstract关键字 然后补上方法体大括号 - 创建子类对象进行使用
五、抽象类中 可以有构造方法 但是父类的构造方法是供子类创建对象时初始化父类成员使用的 不能new父类来使用父类的构造方法
六、抽象类中不一定包含抽象方法 但是有抽象方法的类一定是抽象类 没有抽象方法的类目的就是不想让调用者创建该类的对象
七、抽象类的子类必须覆盖重写抽象父类的所有抽象方法 除非该子类也是抽象类
public abstract class Animal {
public Animal() {
System.out.println("抽象父类构造方法执行!");
}
//这是一个抽象方法 代表吃东西 但是具体吃什么(大括号的内容)不确定
public abstract void eat();
}
public class Cat extends Animal{
public Cat() {
System.out.println("子类构造方法执行!");
}
@Override
public void eat() {
System.out.println("猫吃鱼!");
}
}
public class AbstractClass {
public static void main(String[] args) {
Cat cat = new Cat();
cat.eat();
}
}
接口
一、接口就是一种公共规范标准 只要符合这个规范标准就可以大家通用
二、规范就是多个类的公共规范
三、接口是一种引用数据类型
四、接口最重要的内容就是其中的抽象方法
五、定义一个接口的格式:
public interface 接口名称{
//接口内容
}
六、在任何版本的Java中 接口都能定义抽象方法
接口方法定义格式:public abstract 返回值类型 方法名称(参数列表)
【注】:
- 接口当中的抽象方法修饰符必须是两个固定关键字:public abstract 这两个关键字可以选择性省略
- 方法的三要素可以随意定义
public interface MyInterfaceAbstract {
public abstract void methodAbs1();//这是一个抽象方法
void methodAbs2();
public void methodAbs3();
abstract void methodAbs4();
}
七、编译生成的字节码文件仍然是:.java
转换成.class
文件
八、如果是Java7 那么接口中可以包含的内容有:常量、抽象方法
九、如果是Java8 还可以额外包含:默认方法、静态方法
十、如果是Java9 还可以额外包含:私有方法
十一、接口的使用方法:
- 接口不能直接使用 必须有一个【实现类】来实现接口 实现类格式:
public class 实现类名称 implements 接口名称{
//……
}
- 接口的实现类必须覆盖重写(实现)接口中所有的抽象方法
如何覆盖重写:去掉abstract关键字 加上方法体大括号 - 创建实现类的对象 进行使用
【注】:如果实现类没有覆盖重写接口中所有的抽象方法 那么这个实现类自己就必须是抽象类
public class MyInterfaceAbstractImpl implements MyInterfaceAbstract{
@Override
public void methodAbs1() {System.out.println("这是第1个方法!");}
@Override
public void methodAbs2() {System.out.println("这是第2个方法!");}
@Override
public void methodAbs3() {System.out.println("这是第3个方法!");}
@Override
public void methodAbs4() {System.out.println("这是第4个方法!");}
}
使用:
public class InterfaceClass {
public static void main(String[] args) {
//创建实现类的对象使用
MyInterfaceAbstractImpl interfaceAbstractImpl = new MyInterfaceAbstractImpl();
interfaceAbstractImpl.methodAbs1();
interfaceAbstractImpl.methodAbs2();
}
}
十二、使用接口时需要注意:
- 接口是没有静态代码块或者构造方法的
- 一个类的直接父类是唯一的 但是一个类可以同时实现多个接口
格式:
public class MyInterfaceImpl implements MyInterfaceA,MyInterfaceB{
//覆盖重写所有抽象方法
}
- 如果实现类所实现的多个接口当中 存在重复的抽象方法 那么只需要覆盖重写一次即可
- 如果实现类没有覆盖重写所有接口当中的所有抽象方法 那么实现类就必须是一个抽象类
- 如果实现类所实现的多个接口中 存在重复的默认方法那么实现类一定要对冲突的默认方法进行覆盖重写
- 一个类如果直接父类当中的方法和接口当中的默认方法产生了冲突 优先用父类当中的方法
十三、类与接口的关系:
- 类与类之间是单继承的 直接父类只有一个
- 类与接口之间是多实现的 一个类可以实现多个接口
- 接口与接口之间是多继承的
- 多个父接口中的抽象方法如果重复 没关系
- 多个父接口中的默认方法如果重复 那么子接口必须进行默认方法的覆盖重写 而且要带着default关键字
一、从Java8开始 接口里允许定义默认方法
二、默认方法的定义格式:
public default 返回值类型 方法名称(参数列表){
方法体
}
三、接口中的默认方法可以解决接口升级的问题:假设最开始MyInterfaceDefault接口中只有一个抽象方法methodAbs
public interface MyInterfaceDefault {
public abstract void methodAbs();//抽象方法
//public abstract void methodAbs2();//新添加了一个抽象方法
//新添加的方法改成默认方法就可以解决这个问题
public default void methodDefault() {//可以省略public不写
System.out.println("这是新添加的默认方法!");
}
}
MyInterfaceDefault的两个实现类MyInterfaceDefaultA、MyInterfaceDefaultB都覆盖重写了methodAbs方法 并且两个类都投入使用了,假设MyInterfaceDefault类需要新添加一个抽象方法methodAbs2并投入使用 那么我就需要对两个实现类分别再进行覆盖重写,这样太麻烦了 如果将methodAbs2设置成默认方法 那么就不用重新实现methodAbs2了 默认方法会被实现类继承下去
public class MyInterfaceDefaultA implements MyInterfaceDefault{
@Override
public void methodAbs() {System.out.println("实现了抽象方法,AAAA");}
}
public class MyInterfaceDefaultB implements MyInterfaceDefault{
@Override
public void methodAbs() {System.out.println("实现了抽象方法,BBB");}
@Override
public void methodDefault() {System.out.println("实现类B覆盖重写了接口的默认方法!");}
}
三、接口的默认方法可以通过接口实现类对象 直接调用
四、接口的默认方法也可以被接口实现类进行覆盖重写(MyInterfaceDefaultB)
public class InterfaceClass {
public static void main(String[] args) {
MyInterfaceDefaultA a = new MyInterfaceDefaultA();//创建实现类对象
a.methodAbs();//调用抽象方法 实际运行的是右侧的实现类 实现了抽象方法,AAAA
a.methodDefault();//调用默认方法 如果实现类没有 会向上找接口 这是新添加的默认方法!
MyInterfaceDefaultB b = new MyInterfaceDefaultB();
b.methodAbs();//实现了抽象方法,BBB
b.methodDefault();//实现类B覆盖重写了接口的默认方法!
}
}
静态方法
一、从Java8开始 接口当中允许定义静态方法
二、静态方法格式:
public static 返回值类型 方法名称(参数列表){
方法体
}
提示:就是将abstract或者default换成static 带上方法体即可
public interface MyInterfaceStatic {
public static void methodStatic() {//public可以省略
System.out.println("这是接口的静态方法!");
}
}
实现:
public class MyInterfaceStaticImpl implements MyInterfaceStatic{}
三、接口的静态方法的使用:
- 不能通过接口实现类的对象来调用接口当中的静态方法 因为一个接口可能有多个实现 接口中的静态方法可能产生冲突
- 通过接口名称直接调用其中的静态方法 格式:
接口名称.静态方法名([参数]);
public class InterfaceClass {
public static void main(String[] args) {
MyInterfaceStaticImpl impl = new MyInterfaceStaticImpl();//创建实现类对象
//impl.methodStatic();错误!不能通过接口实现类的对象来调用接口当中的静态方法
MyInterfaceStatic.methodStatic();//通过接口名称直接调用其中的静态方法
}
}
私有方法
一、问题描述:假设一个接口中的好几个方法有公共代码 我们如果想抽取一个共有方法用来解决几个默认方法之间重复代码的问题,但是这个共有方法不能让实现类使用 是专属于这几个默认方法的 所以共有方法应该是私有化的
二、解决方案:从Java9开始 接口当中允许定义私有方法
- 普通私有方法:解决多个默认方法之间重复代码问题
格式:
private 返回值类型 方法名称(参数列表){
方法体
}
- 静态私有方法:解决多个静态方法之间重复代码问题
格式:
private static 返回值类型 方法名称(参数列表){
方法体
}
public interface MyInterfacePrivateB {
public static void methodStatic1() {
System.out.println("静态方法1");
//methodStaticCommon();
}
public static void methodStatic2() {
System.out.println("静态方法2");
//methodStaticCommon();
}
/*java9中这样写就不报错
private static void methodStaticCommon() {
System.out.println("AAA");
System.out.println("BBB");
System.out.println("CCC");
}
*/
}
使用:
public class InterfaceClass {
public static void main(String[] args) {
MyInterfacePrivateB.methodStatic2();
}
}
常量的使用
一、接口当中也可以定义成员变量 但是必须使用public
,static
,final
三个关键字进行修饰 从效果上来看 这其实就是接口的常量
二、格式:public static final 数据类型 常量名称 = 数据值;
public
:谁都可以用static
:写上之后意味着跟对象没关系了final
:不可变
【注】:- 接口当中的常量可以选择性省略
public
,static
,final
,但是 不写也是这样的 - 接口当中的常量必须进行赋值 不能不赋值 因为成员变量有默认值 如果不赋值 默认值一旦放进来就不能变了
- 不能变的常量的名字要全部大写 如果包含多个单词 用_隔开
public class MyInterfaceConst {
//这其实就是一个常量 一旦赋值 不可修改
public static final int NUM_OF_CLASS = 10;
}
三、如何使用:接口名称.常量名称
public class InterfaceClass {
public static void main(String[] args) {
System.out.println(MyInterfaceConst.NUM_OF_CLASS);//10
}
}
多态
一、面向对象三大特征:封装性 继承性 多态性
二、extends继承或者implements实现 是多态性的前提
三、假设父类是人类 子类是学生类和员工类 小明是一个学生 但同时也是一个人,小明是一个对象 这个对象既有学生形态 也有人类形态 一个对象拥有多种形态 这就是对象的多态性
四、代码当中体现多态性 其实就是一句话 父类引用指向子类对象
五、格式:父类名称 对象名 = new 子类名称();
或者:接口名称 对象名 = new 实现类名称();
六、访问成员变量的两种方式:
- 直接通过对象名称访问:看等号左边是谁 优先用谁 没有则向上找 不能往下找
- 间接通过成员方法访问:该方法属于谁优先用谁 没有则向上找
七、只有成员方法才能覆盖重写 方法不能覆盖重写
八、访问成员方法的规则:看new的是谁 就优先用谁 没有则向上找
【注】:
- 成员方法:编译看左 运行看右
- 成员变量:编译看左边 运行还看左边
public class Fu2 {
int num = 10;
public void method() {System.out.println("父类方法!");}
public void methodFu() {System.out.println("父类特有方法!");}
public void showNum() {System.out.println(num);}
}
public class Zi2 extends Fu2{
int num = 20;
int age = 16;
@Override
public void method() {System.out.println("子类方法!");}
@Override
public void showNum() {System.out.println(num);}
public void methodZi() {System.out.println("子类特有方法!");}
}
public class Polymorphism {
public static void main(String[] args) {
//使用多态的写法 左侧父类的引用指向了右侧子类的对象
Fu2 objFu2 = new Zi2();
System.out.println("访问成员方法:==================================");
objFu2.method();//子类方法! 成员方法看右边new的是谁就用谁
objFu2.methodFu();//父类特有方法!子类没有则向上找
objFu2.showNum();//子类没有覆盖重写 就是父:10 子类覆盖重写了 就是子:20
/*编译看左边 左边是Fu2 Fu2中没有methodZi方法 所以编译报错
* objFu2.methodZi();
*/
System.out.println("访问成员变量:==================================");
System.out.println(objFu2.num);//父:10
//System.out.println(objFu2.age);错误!优先使用父类 父类没有age 不会往下找 而会去object类中找
}
}
九、对象的向上转型 其实就是多态写法
十、格式: 父类名称 对象名 = new 子类名称();
含义:右侧创建一个子类对象 把他当做父类来看待
假设父类为Animal类 子类为Cat类 则按格式写为:Animal animal = new Cat();
创建了一只猫 当做动物看待
【注】:向上转型一定是安全的 从小范围的猫转向了大范围的动物
十一、向上转型的弊端:对象一旦向上转型为父类 就被当做父类来看 那么就无法调用子类原本特有的内容
十二、为解决向上转型的弊端 可以将对象向下转型【还原】
十三、对象的向下转型其实就是一个【还原】动作
格式:子类名称 对象名 = (子类名称)父类对象
含义:将父类对象还原为本来的子类对象
Animal animal = new Animal();//本来是猫 向上转型成为动物
Cat cat = new cat();//本来是猫 已经被当做动物了 还原回来成为本来的猫
【注】:
- 必须保证对象本来创建的时候就是猫 才能向下转型成为猫
- 如果对象创建时本来不是猫 现在非要向下转型成为猫 就会报错:
ClassCastException
public abstract class Animal {public abstract void eat();}
public class Cat extends Animal{
@Override
public void eat() {System.out.println("猫吃鱼!");}
//子类特有方法
public void catchMouse() {System.out.println("猫抓老鼠!");}
}
public class Polymorphism {
public static void main(String[] args) {
System.out.println("向上转型:====================================");
//对象的向上转型就是父类引用指向子类对象
Animal animal = new Cat();//本来创建时是一只猫
animal.eat();
//animal.catchMouse();错误!对象一旦向上转型为父类 那么就无法调用子类原本特有的内容
System.out.println("向下转型:====================================");
//向下转型 进行还原
Cat cat = (Cat) animal;
cat.catchMouse();
/*错误写法:
* Dog dog = (Dog) animal;
* 本来new的是一只猫 现在非要当做一条狗
* 编译不会报错 但是运行会出现异常:java.lang.ClassCastException类转换异常
*/
}
}
instanceof
一、instanceof
:判断子类是不是父类的实例对象
二、格式:对象 instanceof 类型
,这将会得到一个布尔值结果 也就是判断前面的对象能不能当做后面类型的实例
public class classInstanceof {
public static void main(String[] args) {
Animal animal = new Cat();
animal.eat();
//如果希望调用子类特有方法需要向下转型
//判断父类引用animal本来是不是Dog
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.watchHouse();
}
//判断animal本来是不是Cat
if (animal instanceof Cat) {
Cat cat = (Cat) animal ;
cat.catchMouse();
}
}
}
权限修饰符
一、java中有四种权限修饰符:
public | protected | (default) | private | |
---|---|---|---|---|
同一个类(我自己) | Y | Y | Y | Y |
同一个包(我邻居) | Y | Y | Y | N |
不同包子类(我儿子) | Y | Y | N | N |
不同包不同类(陌生人) | Y | N | N | N |
注:(defalut)不是关键字default 而是什么都不写
二、定义一个类的时候 权限修饰符规则:
- 外部类:
public
/(default)
- 成员内部类:
public
/protected
/(default)
/private
- 局部内部类:什么都不能写
一、final关键字代表最终的 不可改变的
二、常见四种用法:
- 修饰一个类
- 修饰一个方法
- 修饰一个局部变量(Demo01Final)
- 修饰一个成员变量(Person)
一、格式:
public final class 类名{
……//随便写 没要求
}
二、含义:当前这个类不能有任何的子类 即太监类
注意:final类中的所有成员方法都无法进行覆盖重写(因为只有子类才能覆盖重写 而final没有子类)
public final class Demo01Final {
public void method() {
System.out.println("方法执行!");
}
}
修饰一个方法
一、修饰一个方法的时候 这个方法就是最终方法 即该方法不能覆盖重写
二、格式:
修饰符 final 返回值类型 方法名称(参数列表){
方法体;
}
修饰一个局部变量(Demo01Final)注意:对于类和方法来说
abstract
关键字和final
关键字不能同时使用 因为矛盾 含有abstract
方法的类一定是抽象类,抽象类必须覆盖重写 而final
关键字修饰的方法不能覆盖重写
public final class Demo01Final {
public static void main (String[] args) {
int num1=10;
System.out.println(num1);
num1=20;
System.out.println(num1);
final int num2=200;//一旦使用final来修饰局部变量,那么这个变量就不能进行更改 “一次赋值 终生不变”
//不变的含义:对于基本类型来说 指的是变量当中的数据不可变;
//对于引用类型来说 指的是变量当中的地址值不可变 但是里面的数据可以用setName来改变(Demo01Final Student)
System.out.println(num2);
//num2=100;错误!不能改变
//num2=200;错误!只能赋值一次!
final int num3;
num3=100;//正确!只要有一次赋值即可
System.out.println(num3);
//不变的含义
Student stu1=new Student("赵丽颖");
System.out.println(stu1.getName());//赵丽颖
System.out.println(stu1);//Student@15db9742
stu1=new Student("霍建华");
System.out.println(stu1.getName());//霍建华
System.out.println(stu1);//Student@6d06d69c
final Student stu2=new Student("高圆圆");
System.out.println(stu2.getName());//高圆圆
System.out.println(stu2);//Student@7852e922
//stu2=new Student("赵又廷");错误写法!final的引用类型变量 其中地址值不可更改
stu2.setName("赵又廷");//final的引用类型变量 其中地址值不可更改 但该地址下的内容可以改变
System.out.println(stu2.getName());//赵又廷
System.out.println(stu2);//Student@7852e922
}
}
public class Student {
private String name;
public Student() {}
public Student(String name) {this.name=name;}
public String getName() {return name;}
public void setName(String name) {this.name=name;}
}
修饰一个成员变量
一、对于成员变量来说 如果使用final关键字修饰,那么这个变量也照样是不可变的
二、由于成员变量具有默认值 所以用了final之后必须手动赋值 不会再给默认值了
三、对于final的成员变量 要么使用直接赋值(private final String name="鹿晗"
),要么通过构造方法赋值
四、用构造方法赋值时,必须保证类当中所有重载的构造方法,都对final的成员变量赋值
public class Person {
private final String name/*="鹿晗"*/;
public Person() {
name = "关晓彤";
//正常情况下此处没有方法体 采用构造方法赋值,为了保证类当中所有重载的构造方法,
//都对final的成员变量赋值 故此处对name赋值
}
public Person(String name) {this.name = name;}
public String getName() {return name;}
/* public void setName(String name) {this.name = name;}
正常情况下此处不能被注释 但是由于name是final类型 所以不能对final赋值 所以不能重新设置name
*/
}
内部类
一、内部类:如果一个事物的内部包含另一个事物,那么这就是一个类内部包含另一个类,例如:身体和心脏的关系、汽车和发动机的关系
二、分类:
- 成员内部类
- 局部内部类(包含匿名内部类)
一、定义格式:
修饰符 class 外部类名称{
修饰符 class 内部类名称{
……
}
……
}
注意:内用外,随意访问;外用内,借助内部类对象
二、如何使用成员内部类:
- 间接方式:在外部类的方法当中使用内部类 然后main只是调用外部类的方法
- 直接方式:【
外.内
】创建内部类的对象,即:外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
public class Body { //外部类
public class Heart{ //成员内部类
public void beat() { //内部类的方法
System.out.println("心脏跳动");
System.out.println("我叫:"+name); //正确写法!内部类在外部类中 可以随意使用外部类的东西
}
}
private String name; //外部类的成员变量
public String getName() {return name;}
public void setName(String name) {this.name=name;}
public void method() { //外部类的方法
System.out.println("外部类的方法");
new Heart().beat();//间接方式
}
}
public class InnerClass {
public static void main(String[] args) {
System.out.println("间接方式使用成员内部类:");
Body body = new Body();//外部类的对象
body.method();//通过外部类的对象 调用外部类的方法 里面间接在使用内部类Heat
System.out.println("直接方式使用成员内部类:");
Body.Heart heart = new Body().new Heart();//直接方式
heart.beat();
Outer.Inner obj = new Outer().new Inner();
obj.methodInner();
System.out.println("使用局部内部类:");
ChengYuan chengyuan = new ChengYuan();
chengyuan.methodChengYuan();
}
}
局部内部类
一、内部类的同名变量访问:
- 格式:
外部类名称.this.外部类成员变量名
- 如果一个类是定义在一个方法内部的 那么这就是一个内部类
- 局部:只有当前的方法才能使用他 出了这个方法外面就不能用了
public class Outer {
int num = 10;//外部类的成员变量
public class Inner {
int num = 20;//内部类的成员变量
public void methodInner() {
int num = 30;//内部类方法的局部变量
System.out.println(num);//内部类方法的局部变量30,就近原则
System.out.println(this.num);//内部类的成员变量20
System.out.println(Outer.this.num);//外部类的成员变量10
}
}
}
二、定义格式:
修饰符 class 外部类名称{
修饰符 返回值类型 外部类方法名称(参数列表){
class 局部内部类名称{
……
}
}
}
三、使用:通过调用局部内部类所在的方法
public class ChengYuan {
public void methodChengYuan() {
class NeiBuLei{//局部内部类 只能在methodChengYuan()当中使用
int num = 10;
public void methodNeiBuLei() {System.out.println(num);}
}
NeiBuLei neibulei = new NeiBuLei();
neibulei.methodNeiBuLei();
}
}
四、局部内部类如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效final的】,即 保证这个局部变量的值不变
注意:从Java8开始 只要局部变量的值可以保证不变 那么final关键字可以省略
五、原因:
- new出来的对象在堆内存当中
- 局部变量跟着方法走 在栈内存当中
- 方法运行结束后 立刻出栈 局部变量就会立刻消失
- 但是new出来的对象会在堆当中持续存在 直到垃圾回收,有可能局部变量消失了 new出来的对象还在 而对象还需要使用局部变量 可是局部变量已经消失了 这就使得从一开始就把局部变量的值,copy给对象 所以局部变量不能变 因为对象一直拿到的都是最开始copy的值
public class MyOuter {
public void methodMyOuter() {
int num = 10;//局部变量 必须保证他的值不变
class MyInner{
public void methodInner() {
System.out.println(num);
}
}
}
}
匿名内部类
一、匿名内部类:如果接口的实现类(或者是父类的子类)只需要使用唯一的一次 那么就可以省略掉该类的定义,而改为使用匿名内部类(MyInterface MyInterfaceImpl DemoMain)
二、定义格式:
接口名称 对象名 = new 接口名称(){
//覆盖重写接口名称的所有抽象方法
};
new 接口名称(){……}
解析:
- new代表创建对象的动作
- 接口名称就是匿名内部类需要实现哪个接口
- {……}这才是匿名内部类的内容
注意:
- 匿名内部类在创建对象的时候 只能使用唯一一次 如果希望多次创建对象 而且覆盖重写的内容一样 那么就必须使用单独定义的实现类了
- 匿名对象:在【调用方法】的时候 只能调用一次 如果希望同一个对象 调用多次方法 那么必须给对象起个名字
三、匿名内部类是省略了【实现类/子类名称】,但是匿名对象是省略了【对象名称】
四、匿名对象和匿名内部类不是同一个东西
public interface MyInterface {void methodMyInterface();//抽象方法}
public class MyInterfaceImpl implements MyInterface{
@Override
public void methodMyInterface() {System.out.println("实现类覆盖重复写了方法!");}
}
public class DemoMain {
public static void main(String[] args) {
MyInterfaceImpl impl = new MyInterfaceImpl();
impl.methodMyInterface();//接口的实现类只用了这一次 而需要新建一个MyInterfaceImpl.java文档 对接口进行覆盖重写 太麻烦了 可以使用匿名内部类
System.out.println("使用匿名内部类:");
MyInterface obj = new MyInterface() {//匿名内部类的名字是obj
@Override
public void methodMyInterface() {
System.out.println("匿名内部类实现了方法!");
}
};
obj.methodMyInterface();
System.out.println("使用匿名对象!");
/*使用匿名内部类 而且省略了对象名称 也是匿名对象*/
new MyInterface() {
@Override
public void methodMyInterface() {System.out.println("匿名内部类实现了方法!");}
}.methodMyInterface();
}
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)