权限修饰符:是用来控制一个成员能够被访问的范围的。
可以修饰:成员变量、方法、构造方法、内部类。
巧计举例:
private--------私有的----------相当于私房钱,只能自己用
默认---------------------------相当于只能在家里面用
protected-------受保护的-------相当于亲戚朋友也能用
public----------公共的---------相当于全世界的人都能用
权限修饰符的使用规则
实际开发中,一般只用private和public
a、成员变量私有
b、方法公开
特例:如果方法中的代码是抽取其他方法中共性代码,这个方法一般也私有
代码块分为三类:局部代码块、构造代码块、静态代码块。
局部代码块
作用:提前结束变量的生命周期。(技术已经被淘汰)
写在方法里面的一对大括号,作用是提前结束变量的生命周期(因为变量的作用域是外面的一层大括号的范围内)
构造代码块:
作用:抽取构造代码方法中的重复代码(不够灵活)
下面代码有两个构造方法,空参构造和全参构造,这两个构造方法中都打印了一句相同的话(打印语句在两个构造方法中重复了)此时可以把重复的代码抽取出来放在构造代码块当中
下面是抽取后的代码:
若以后遇到在一个对象类中,构造方法并不是都有输出的时候(有的有输出,有的没有输出,此时不能用构造代码块技术),可以用以下代码来解决(用this调用其他的构造方法;或者把输出抽取成方法,然后需要的就调用)
package GouZaoDaiMaKuai;
public class Student {
private String name;
private int age;
/**构造代码块:
* 1、写在成员位置的代码块,类中方法外
* 2、作用:可以把多个构造方法中重复的代码抽取出来
* 3、执行时机:我们在创建本类对象的时候会先执行构造代码块再执行构造方法
*/
{
System.out.println("构造代码块");
}
public Student() {
System.out.println("无参构造");
}
public Student(String name, int age) {
System.out.println("有参构造");
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;
}
}
package GouZaoDaiMaKuai;
public class Test {
public static void main(String[] args) {
//创建对象s1(无参构造)
Student s1 = new Student();
//创建对象s1(有参构造)
Student s2 = new Student("张三",23);
}
}
输出为:
构造代码块
无参构造
构造代码块
有参构造
静态代码块(重点)
特点:适用于数据初始化,且该代码块只执行一次。
位置处于:类中方法外。
可以用来添加初始账户(即数据初始化),如下:利用静态代码块,执行App类的时候,就自动创建了一个账户,该用户可以用来直接登录
如果用下列方法进行数据的初始化,其实是有弊端的,因为main是一个方法,main方法也是可以被调用的,每次被调用都会被添加新的重复的用户,会导致程序出现bug的。
package GouZaoDaiMaKuai;
public class Student {
private String name;
private int age;
//执行时机:随着类的加载而加载的,并且只执行一次
static {
System.out.println("静态代码块执行了");
}
public Student() {
System.out.println("无参构造");
}
public Student(String name, int age) {
System.out.println("有参构造");
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;
}
}
package GouZaoDaiMaKuai;
public class Test {
public static void main(String[] args) {
//创建对象
Student s1 = new Student();
Student s2 = new Student("李华",30);
}
}
运行结果如下:
静态代码块执行了
无参构造
有参构造
抽象类解决的问题:例如父类为Person,子类为Teacher和Student且都有work方法,但是教师和学生的工作不太一样。之前的做法是把Work方法抽取到父类Person当中,该方法的方法体只能先随便写一个,然后让子类进行重写。但是这样存在弊端,因为如果子类忘记重写Work方法,就会出现bug。为了避免这样的情况出现,可以用下面的方法对Work方法进行抽象,(抽象后的方法不用写方法体)被抽象的方法如果没有被子类重写,就会报错(即这种方法相当于在提醒开发者不要忘记重写方法)
抽象类的作用是什么?
答:抽取共性时,无法确定方法体,就把方法定义为抽象的。强制让子类按照某种格式重写。抽象方法所在的类,必须是抽象类。
抽象方法与抽象类
抽象方法:将共性的行为(方法)抽取到父类之后。由于每一个子类执行的内容是不一样的,所以在父类中不能确定具体的方法体。该方法就可以定义为抽象方法。
抽象类:如果一个类中存在抽象方法,那么该类就必须声明为抽象类。
抽象类和抽象方法的定义格式如下:
1、抽象方法的定义格式:
public abstract 返回值类型 方法名(参数列表);
注意:参数列表后面直接是分号,没有大括号结构体。
例如: public abstract void show(String name);
2、抽象类的定义格式:
public abstract class 类名{
属性;
行为;
}
例如: public abstract class Person{ 结构体 }
抽象类和抽象方法的注意事项
1、抽象类不能实例化。(实例化是指创建对象),但是可以被继承。
2、抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。
3、抽象类可以有构造方法。
4、抽象类的子类:要么重写抽象类中的所有抽象方法,要么本身(子类)也是一个抽象类。
5、抽象类的好处:
a、可以避免父类对某个方法进行实现。
b、子类继承父类就必须重写抽象类中的抽象方法。
6、抽象类中可以有静态方法,直接通过类名访问。
练习实践案例:编写带有抽象类的标准JavaBean类
package LianXi01;
public abstract class Animal {
private String name;
private int age;
public Animal() {
}
public Animal(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;
}
//因为各种动物吃的食物不一样,所以对eat方法进行抽象
//抽象方法所在的类必须是抽象类,所以Animal需要改为抽象类
public abstract void eat();
public void dtink(){
System.out.println("喝水");
}
}
package LianXi01;
public class Frog extends Animal{
public Frog() {
}
public Frog(String name, int age) {
super(name, age);
}
//父类中的抽象方法,子类必须进行重写
@Override
public void eat(){
System.out.println("吃虫子");
}
}
package LianXi01;
public class Dog extends Animal{
public Dog() {
}
public Dog(String name, int age) {
super(name, age);
}
@Override
public void eat(){
System.out.println("吃骨头");
}
}
package LianXi01;
public class Sheep extends Animal{
public Sheep() {
}
public Sheep(String name, int age) {
super(name, age);
}
@Override
public void eat(){
System.out.println("吃草");
}
}
package LianXi01;
public class test {
public static void main(String[] args) {
//创建对象
Frog f = new Frog("小绿",1);
System.out.println(f.getName()+","+f.getAge());
f.eat();
f.dtink();
}
}
运行结果如下:
小绿,1
吃虫子
喝水
1、设计模式:是一种反复使用,多人知晓,经过分析编译代码、代码设计经验的总结。
2、使用设计模式的目的:为了让代码更容易被别人理解,保证代码的可靠性和程序的重用性。
3、模板设计模式:把抽象类整体看作一个模板,模板中不能决定的内容使用抽象方法表示,让使用模板的类【抽象类的子类】去重写抽象方法,实现需求。
4、好处:模板设计模式优势在于,已经设定好通用的结构,使用者只需要直接拿着去用就可以了,只需要关注自己实现的内容即可。
例如下面:如果定义游泳行为,因为兔子不会游泳,所以游泳行为不可以抽取到父类当中,然而在子类当中各自单独定义游泳行为的时候,方法名可能出现方法不统一的现象(例如下面的swim和swimming方法),为了解决这个问题,接口就出现了。接口就是一种规则。
创建一个接口(相当于创建一种规则),在接口中就能定义游泳的抽象方法,然后让青蛙类和狗类重写接口中的swim方法。
接口的应用:接口不代表一类事务,它是一种规则,它是对行为的抽象。
(1)、接口用关键字interface来定义。
public interface 接口名 {}
(2)、接口不能实例化。
(3)、接口和类之间是实现关系,通过implements关键字表示
public class 类名 implements 接口名 {}
(4)、接口的子类(实现类):要么重写接口中的所有抽象方法,要么是抽象类。
(5)、接口中的方法都是抽象方法。
注意事项:
接口的创建流程:右击包名,选择新建Java类,然后不选class,而是选择Interface,然后回车。
练习实践案例:编写带有接口和抽象类的标准JavaBean类
父类代码:
package LianXi01;
public abstract class Animal {
private String name;
private int age;
public Animal() {
}
public Animal(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 abstract void eat();
}
接口代码:
package LianXi01;
public interface Swim { //接口
public abstract void swim(); //接口中的抽象方法
}
兔子类代码:
package LianXi01;
public class Rabbit extends Animal{
public Rabbit() {
}
public Rabbit(String name, int age) {
super(name, age);
}
@Override
public void eat(){
System.out.println("兔子在吃胡萝卜");
}
}
青蛙类代码:
package LianXi01;
public class Frog extends Animal implements Swim{
public Frog() {
}
public Frog(String name, int age) {
super(name, age);
}
//父类中的抽象方法,子类必须进行重写
@Override
public void eat(){
System.out.println("青蛙在吃虫子");
}
@Override
public void swim() {
System.out.println("青蛙在蛙泳");
}
}
狗类代码L:
package LianXi01;
public class Dog extends Animal implements Swim{
public Dog() {
}
public Dog(String name, int age) {
super(name, age);
}
//父类中的方法和接口中的方法可以利用快捷方式同时添加重写
//当implements处报错的时候,按住alt+enter,选择implement methods选项
//然后crlt+A,全选,最后点击回车确认即可
@Override
public void eat(){
System.out.println("狗吃骨头");
}
@Override
public void swim() {
System.out.println("狗刨");
}
}
测试类代码:
package LianXi01;
public class test {
public static void main(String[] args) {
//创建对象
Frog f = new Frog("小绿",1);
System.out.println(f.getName()+","+f.getAge());
f.eat();
f.swim();
System.out.println("-----------");
Rabbit r = new Rabbit("小白",3);
System.out.println(r.getName()+","+r.getAge());
r.eat();
//r.swim(); r调用不了swim方法,因为兔子类没有调用游泳接口
}
}
输出结果如下:
小绿,1
青蛙在吃虫子
青蛙在蛙泳
-----------
小白,3
兔子在吃胡萝卜
(1)、类和类的关系:继承关系,只能单继承,不能多继承,但是可以多层继承。
(2)、类和接口的关系:实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口。
接口1代码
package ChengYuanTeDian;
public interface Inter1 {
public abstract void method1();
public abstract void method2();
public abstract void method3();
}
接口2代码
package ChengYuanTeDian;
public interface Inter2 {
public abstract void method1();
public abstract void method2();
public abstract void method3();
public abstract void method4();
}
创建一个实现类,调用两个接口
package ChengYuanTeDian;
public class Intermpl implements Inter1,Inter2{
//当两个接口中有重复的方法时,只需要重写一次即可
//例如下面案例中,method1,method2,method3在Inter1、Inter2中都有
//但下面案例中就重写了一次
@Override
public void method1() {
System.out.println("method1");
}
@Override
public void method2() {
System.out.println("method2");
}
@Override
public void method3() {
System.out.println("method3");
}
@Override
public void method4() {
System.out.println("method4");
}
}
测试类代码
package ChengYuanTeDian;
public class Test {
public static void main(String[] args) {
System.out.println();
}
}
(3)、接口和接口的关系:继承关系,可以单继承,也可以多继承。(如果实现类实现的是最下面的子接口的话,那么就需要重写所有的抽象方法)
接口1代码
package ChengYuanTeDian;
public interface Inter1 {
public abstract void method1();
}
接口2代码
package ChengYuanTeDian;
public interface Inter2 {
public abstract void method2();
}
接口3代码(继承了接口1和接口2)
package ChengYuanTeDian;
public interface Inter3 extends Inter1,Inter2{
public abstract void method3();
}
接口的实现类代码
package ChengYuanTeDian;
public class Intermpl implements Inter3{
//由于接口3继承了接口2和接口1,所以实现类实现接口3的时候,接口1和接口2中的方法也必须得重写。
@Override
public void method3() {
}
@Override
public void method1() {
}
@Override
public void method2() {
}
}
测试类代码
package ChengYuanTeDian;
public class Test {
public static void main(String[] args) {
System.out.println();
}
}
综合案例1:编写带有接口和抽象类的标准JavaBean类。
我们现在有乒乓球运动员和篮球运动员,乒乓球教练和篮球教练。为了出国交流,跟乒乓球相关的人员都需要学习英语。请用所有知识分析,在这个案例中,哪些是具体类,哪些是抽象类,哪些是接口?
思路1
思路2:
通过思路2进行代码书写
Person类代码如下:
package JieKouLianXi;
//因为现在我不想让外界直接创建人的对象
//因为直接创建顶层父类人的对象此时是没有意义的
//所以我就把他写为抽象的
public abstract 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;
}
}
运动员类代码如下:
package JieKouLianXi;
public abstract class YunDongYuan extends Person{
public YunDongYuan() {
}
public YunDongYuan(String name, int age) {
super(name, age);
}
//因为学习的内容不一样,所以study定义为抽象的方法
public abstract void study();
}
教练类代码如下:
package JieKouLianXi;
public abstract class JiaoLian extends Person{
public JiaoLian() {
}
public JiaoLian(String name, int age) {
super(name, age);
}
//因为教练教的内容不一样,所以teach定义为抽象的方法
public abstract void teach();
}
接口代码如下:
package JieKouLianXi;
public interface English {
public abstract void speakEnglish();
}
乒乓球运动员类代码如下:
package JieKouLianXi;
//乒乓球运动员继承父类运动员,调用接口学英语
public class PingPangYDY extends YunDongYuan implements English{
//一定不要忘记构造方法的书写
public PingPangYDY() {
}
public PingPangYDY(String name, int age) {
super(name, age);
}
//重写接口中的方法
@Override
public void speakEnglish() {
System.out.println("乒乓球运动员在说英语");
}
//重写父类中的抽象方法
@Override
public void study() {
System.out.println("乒乓球运动员在学习如何打乒乓球");
}
}
篮球运动员类代码如下:
package JieKouLianXi;
public class LanQiuYDY extends YunDongYuan{
public LanQiuYDY() {
}
public LanQiuYDY(String name, int age) {
super(name, age);
}
@Override
public void study() {
System.out.println("蓝球运动员在学习如何打篮球");
}
}
乒乓球教练类代码如下:
package JieKouLianXi;
public class PingPangQiuJL extends JiaoLian implements English{
public PingPangQiuJL() {
}
public PingPangQiuJL(String name, int age) {
super(name, age);
}
@Override
public void speakEnglish() {
System.out.println("乒乓球教练在说英语");
}
@Override
public void teach() {
System.out.println("乒乓球教练在教运动员打乒乓球");
}
}
篮球教练类代码如下:
package JieKouLianXi;
public class LanQiuJL extends JiaoLian{
public LanQiuJL() {
}
public LanQiuJL(String name, int age) {
super(name, age);
}
@Override
public void teach() {
System.out.println("蓝球教练在教运动员打篮球");
}
}
测试类代码如下:
package JieKouLianXi;
public class Test {
public static void main(String[] args) {
PingPangYDY ppydy = new PingPangYDY("李华",24);
System.out.println(ppydy.getName()+","+ppydy.getAge());
ppydy.study();
ppydy.speakEnglish();
}
}
输出结果如下:
李华,24
乒乓球运动员在学习如何打乒乓球
乒乓球运动员在说英语
(1)、JDK8开始接口中新增加的方法
JDK7以前:接口中只能定义抽象方法。
JDK8的新特性:接口中可以定义有方法体的方法。(默认、静态)
A、接口中定义有方法体的默认方法。
实践案例如下:
package JieKou;
public interface InterA {
/**接口中的默认方法
* 定义格式:public default 返回值类型 方法名(参数列表){ }
*
* 接口中默认方法的注意事项:
* 1、默认方法不是抽象方法,所以不强制被重写。但是如果被重写,重写的时候去掉default关键字
* 2、public可以省略,default不能省略
* 3、如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写
*/
//定义抽象方法
public abstract void method();
//定义默认方法
public default void show(){
System.out.println("InterA接口中的默认方法------show");
}
}
package JieKou;
public interface InterB {
//定义默认方法
public default void show(){
System.out.println("InterB接口中的默认方法------show");
}
}
package JieKou;
public class Intermpl implements InterA ,InterB{
//默认方法不是抽象方法,所以不强制被重写
@Override
public void method() {
System.out.println("实现类重写的抽象方法");
}
//在实现类中,书写接口中的默认方法的时候,可以利用快捷方式:方法名+回车
//当接口实现类同时调用接口A和接口B的时候,show方法必须要重写,不然测试类Test创建对象后,调用
//show方法的时候分不清调用的是接口A中的还是接口B中的,此时就会报错
//然而重写show方法之后,测试类中调用的show方法是重写后的
@Override
public void show() {
System.out.println("重写接口中的默认方法");
}
}
package JieKou;
public class Test {
public static void main(String[] args) {
Intermpl ii = new Intermpl();
ii.method();
ii.show();
}
}
运行结果如下:
实现类重写的抽象方法
重写接口中的默认方法
B、接口中定义有方法体的静态方法。
实践案例如下:
package JieKou;
public interface Inter {
//定义一个抽象方法
public abstract void menoth();
//定义静态方法
public static void show(){
System.out.println("InterA接口中的静态方法");
}
}
package JieKou;
public class Intermpl implements Inter {
@Override
public void menoth() {
System.out.println("Intermpl重写的抽象方法");
}
//静态方法不能被重写,如果重写,系统就会报错
//下面不是show方法的重写,因为静态方法不能被重写,此处只是定义了一个和接口中具有同名的静态方法而已
public static void show(){
System.out.println("实现类中的静态方法");
}
}
package JieKou;
public class Test {
public static void main(String[] args) {
//调用接口中的静态方法,调用格式: 接口名.方法名();
Inter.show();
//调用实现类中的静态方法,调用格式:实现类名字.方法名();
Intermpl.show();
/**重写的概念
* 子类把从父类继承下来的虚方法表里面的方法进行覆盖了,这才叫重写。
* 注意:静态的、私有的、最终的是不会被添加到虚方法表里面的,因此这三类不能被重写
*/
}
}
运行结果如下:
InterA接口中的静态方法
实现类中的静态方法
JDK9的新特性:接口中可以定义私有方法。
私有方法分为两种:普通的私有方法,静态的私有方法
普通的私有方法是给默认方法使用的,静态的私有方法是给静态方法使用的。
实践案例如下:
(2)、接口的应用
方式1:
方式2:
接口的应用(小结):
1、接口代表规则,是行为的抽象。想要让哪个类拥有一个行为,就让这个类实现对应的接口就可以了。
2、当一个方法的参数是接口时,可以传递接口所有实现类的对象,这种方式称之为接口多态。
(3)、适配器设计模式
实践案例如下:
package Dome;
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();
}
package Dome;
//创建一个适配器调用接口,然后对接口中所有的方法进行空实现
//空实现是指:实现了,但是方法体不写,让方法体空着
//因为适配器对接口中的所有方法进行了空实现,所以外界调用适配器没有意义,因此适配器应改为抽象类
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() {
}
}
package Dome;
public class Intermpl extends InterAdapter{
//需要用到哪个方法,就重写哪个方法就可以了
@Override
public void method5() {
System.out.println("只要用第五个方法");
}
}
package Dome;
public class Test {
public static void main(String[] args) {
Intermpl m1 = new Intermpl();
m1.method5();
}
}
运行结果如下:
只要用第五个方法
适配器设计模式(小结)
A、当一个接口中抽象方法过多,但是我只要使用其中一部分的时候,就可以适配器设计模式。
B、书写步骤如下:
a、编写中间类XXXAdapter,实现对应的接口。
b、对接口中的抽象方法进行空实现。
c、让真正的实现类继承中间类,并重写需要用的方法。
d、为了避免其他类创建适配器类的对象,中间的适配器类用abstract进行修饰。
C、如果真正的实现类还有一个父类H,但是Java中不能多继承,此时让中间类XXXAdapter去继承父类H就能解决此问题。(即让真正的子类与父类H实现间接继承)
类的五大成员:属性、方法、构造方法、代码块、内部类。
内部类的定义:在一个类里面,再定义一个类。
package LianXi;
public class Car {
String carName;
int carAge;
String carColor;
public void show2(){
//
System.out.println(carName);
//外部类要访问内部类成员,必须创建内部类对象
Engine e = new Engine(); //创建内部类对象
//e.engineName = "汽车引擎";
//e.show();
System.out.println(e.engineName); //如果不创建内部类对象,这个输出就会报错
}
//定义内部类
class Engine{
String engineName;
int engineAge;
public void show(){ //内部类可以直接访问外部类的成员,包括外部类的私有成员
System.out.println(engineName);
System.out.println(carName);
}
}
}
package LianXi;
public class Test {
public static void main(String[] args) {
Car c = new Car();
c.carName = "宾利";
c.carAge = 1;
c.carColor = "黑色";
c.show2();
}
}
成员内部类、静态内部类、局部内部类,这三个只做了解,匿名内部类需要重点掌握。
4.2.1 成员内部类:
A、成员内部类的代码如何书写?以及编写成员内部类的注意点
a、定义:写在成员位置的,属于外部类的成员
b、成员内部类可以被一些修饰符所修饰,比如:private、默认、protected、public、static等
c、在成员内部类里面,JDK16之前不能定义静态变量,JDK16开始才可以定义静态变量。
d、内部类可以访问外部类中的所有成员,包括私有成员。
e、外部类中访问内部类中的成员,必须创建内部类对象。
B、如何创建成员内部类的对象?(获取成员内部类对象的两种方式)
方式1:当成员内部类被private修饰时。在外部类中编写方法,对外提供内部类的对象。
方式2:当成员内部类被非私有修饰时,直接创建对象。直接创建格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
方式2:
Outer.Inter oi = new Outer().new Inter();
实践案例代码如下:
package LianXi;
public class Outer {
String name;
//定义内部类
private class Inter{
}
//当内部类被私有化时,让外部类Outer编写方法,对外提供内部类对象
public Inter getInstance(){
return new Inter();
}
}
package LianXi;
public class Test {
public static void main(String[] args) {
//1、如果内部类没有被私有化,则可以直接创建对象
//直接创建对象的格式如下:
//Outer.Inter oi = new Outer().new Inter();
//2、当内部类被私有化的时候,只能通过下面的方式创建内部类
//2.1、先创建外部类对象
Outer o = new Outer();
//再调用外部类中的getInstance()方法获取到创建内部类对象,并赋值给变量ii
Object ii = o.getInstance();
//2.2、或者直接利用输出语句输出o.getInstance()
System.out.println(o.getInstance());
}
}
C、成员内部类如何获取外部类的成员变量?
package LianXi;
public class Outer {
private int a = 10;
//定义内部类
class Inter{
private int a = 20;
public void show(){
/**变量的调用
* 调用内部类中的方法中的变量:变量名 比如:a
* 调用内部类中方法外的变量:this.变量名 比如:this.a
* 调用外部类的变量:外部类类名.this.变量名 比如:Outer.this.a
*/
int a = 30;
System.out.println(a); //30
System.out.println(this.a); //20
System.out.println(Outer.this.a); //10
}
}
}
package LianXi;
public class Test {
public static void main(String[] args) {
//创建内部类的对象,并调用内部类中的show方法
Outer.Inter oi = new Outer().new Inter();
oi.show();
}
}
输出结果如下:
30
20
10
4.2.2 静态内部类
静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问非静态的需要创建外部类的对象
访问特点:
1、是外部类的静态成员,可以直接通过类名去访问,而不需要创建外部类对象。
2、静态内部类的非静态成员,需要等所在的内部类对象创建之后,才能被调用。
3、一个类是否需要创建对象,不取决于该类是否是静态,而是取决于要访问的成员是否是静态。
静态内部类访问外部类,实践案例代码如下:
package LianXi;
public class Outer01 {
int a = 10;
static int b = 20;
//静态内部类
static class Inter{
public void show1(){
System.out.println("非静态的方法被调用了");
//System.out.println(a); 语句会报错,因为静态内部类只能访问外部类中
//的静态变量和静态方法,而不能访问外部类中非静态变量和静态方法
System.out.println(b);
}
public static void show2(){
System.out.println("静态的方法被调用了");
//如果想要访问外部类中非静态的,需要创建外部类的对象
Outer01 o = new Outer01();
System.out.println(o.a);
}
}
}
外部类访问静态内部类,实践案例代码如下:
package LianXi;
public class Outer02 {
//创建静态内部类
static class Inter002{
public void work(){
System.out.println("非静态的方法被调用了");
}
public static void work2(){
System.out.println("静态的方法被调用了");
}
}
}
package LianXi;
public class Test {
public static void main(String[] args) {
//调用非静态方法时,先创建对象,用对象调用
//创建静态内部类对象的格式:
// 外部类名.内部类名 对象名 = new 外部类名.内部类名();
Outer02.Inter002 oi02 = new Outer02.Inter002();
oi02.work();
//调用静态方法时,直接调用,格式:
// 外部类名.内部类名.方法名();
Outer02.Inter002.work2();
}
}
运行结果如下:
非静态的方法被调用了
静态的方法被调用了
4.2.3 局部内部类
1、将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量。
2、外界是无法直接使用,需要在方法内部创建对象并使用。
3、该类可以直接访问外部类的成员,也可以访问方法内的局部变量。
4、能修饰局部变量的都能用来修饰局部内部类,不能修饰局部变量的就不能修饰局部内部类。
实践案例代码如下:
package LianXi;
public class Outer01 {
int b = 20;
public void show(){
int a = 10;
//局部内部类
class Inter{
String name;
int age;
//定义一个方法
public void method1(){
System.out.println(a); //该类可以直接访问方法内的局部变量 10
System.out.println(b); //该类可以直接访问外部类的成员 20
System.out.println("局部内部类中的method1方法");
}
/*//局部内部类中,不能定义静态方法,会报错
public static void method2(){
System.out.println("局部内部类中的method2方法");
}*/
}
//外界是无法直接使用局部内部类的,需要在方法内部创建对象并使用
//创建局部内部类的对象
Inter i = new Inter();//此处会调用构造方法,而method1()不是构造方法
System.out.println(i.name); //null
System.out.println(i.age); //0
i.method1(); //调用method1方法,打印方法中的三个输出
}
}
package LianXi;
public class Test {
public static void main(String[] args) {
//调用show方法,让代码执行
Outer01 o = new Outer01();
o.show(); //局部内部类在show方法中,执行show方法就执行了内部类
}
}
运行代码如下:
null
0
10
20
局部内部类中的method1方法
4.2.4 匿名内部类
实践案例代码如下:
package NiMing;
public interface Swim {
public abstract void swim();
}
package NiMing;
public abstract class Animal {
public abstract void eat();
}
package NiMing;
public class Test {
public static void main(String[] args) {
System.out.println();
//编写匿名内部类的代码,此处相当于new(创建)了Swim的实例化对象(实例化)
//接口实例化的过程中需要书写接口中所有的抽象方法
new Swim(){
@Override
public void swim() {
System.out.println("重写了游泳的方法");
}
};
//编写匿名内部类的代码,此处相当于new(创建)了Animal的子类对象(继承)
//继承的过程中,子类需要重写父类中所有的抽象方法
new Animal(){
@Override
public void eat() {
System.out.println("重写了eat方法");
}
};
//执行静态方法,相当于method(参数); 参数为对象(此处对象指匿名内部类)
method(
new Animal(){
@Override
public void eat() {
System.out.println("狗吃骨头");
}
}
);
}
//创建静态方法
public static void method(Animal a){
a.eat();
}
}
小结: