Java面向对象
一、面向对象三大特性
1.封装
封装(encapsulation)就是把抽象出的数据(属性)和对数据的操作(方法)封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(方法),才能对数据进行操作。
说的简单点就是外部不可以直接访问到内部属性,只能通过暴露的出来的某些方法来对这些属性进行操作
说这个之前,先来回顾一下java中的访问修饰符
公开级别的:用public进行修饰,不同类和不同包都可以访问;
受保护级别的:用protected修饰,对子类和同一包中的类公开;
默认级别的:不用任何修饰符修饰,向同一个包的类公开;
私有级别的:用private修饰,只有类本身可以访问,不对外公开;
访问级别 | 同类 | 同包 | 子类 | 不同包 |
---|---|---|---|---|
public | 可以 | 可以 | 可以 | 可以 |
protected | 可以 | 可以 | 可以 | 不可以 |
默认 | 可以 | 可以 | 不可以 | 不可以 |
private | 可以 | 不可以 | 不可以 | 不可以 |
看看以前在编写的类的问题
public class Person {
public String name;
public int age;
public String sex;
}
这种方式就是很早以前的书写方式,这样外部可以直接访问到内部属性。这样一来,可能引起对数据安全性与可靠性的破坏。
public class Person {
private String name;
private int age;
private String sex;
//有参构造器
public Person(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
// 无参构造器
public Person() {
}
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 String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
而这种方式,私有化类中的属性,通过暴露每个属性对应的setter/getter方法对属性进行访问,这样一来保证了其中的安全合理性。
在上面的代码中,还有一个有参构造和无参构造,有参构造就是可以在创建对象的时候,将对象的属性直接传入,以免后面在调用set方法进行赋值。
2.继承
为什么需要继承?
当前有两个类:一个小学生类,一个大学生类,这两个类有很多相同的属性和方法,这个时候就需要一个基类(学生类),用户包含这些相同的属性或者方法,然后在创建出小学生类和大学生类,这两个都会拥有学生类的相关属性和方法。
继承的引出就是为了解决代码复用的问题,假如我们创建一个小学生类和一个大学生类,这样一来,两个类中的相同属性就太多了,代码的冗余太高了。
public class Student {
private String name;
private int age;
private int score;
public Student(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
public Student() { }
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 int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
public void showInfo(){
System.out.println("姓名:"+name+",年龄:"+age+",得分:"+score);
}
}
这是父类student类
public class Pupil extends Student{
public Pupil(String name, int age, int score) {
super(name, age, score);
}
public Pupil() {
}
public void show(){
System.out.println("[ "+getName()+"--->"+getAge()+"--->"+getScore()+" ]");
}
}
这是小学生类
public class College extends Student{
public College(String name, int age, int score) {
super(name, age, score);
}
public College() {
}
public void show(){
System.out.println("[ "+getName()+"--->"+getAge()+"--->"+getScore()+" ]");
}
}
这是大学生类
然后看看测试
public class ExtendsAppTest {
public static void main(String[] args) {
Pupil pupil = new Pupil("小明",9,56);
College college = new College("小红",20,98);
pupil.showInfo();
pupil.show();
college.showInfo();
college.show();
}
}
可以看出来,在Pupil类和College类中,公用了父类中的name,age,score属性,在各自的构造方法中都调用了super关键字为父类的属性进行赋值,达到了公用的目的。
从这个小案例中可以看出,①整体代码的复用性提高了;②代码的可扩展性提高了;③一定程度上代码的冗余度降低了;
关于继承你需要知道几点:
- 子类继承了父类所有的非私有的属性和方法,这些属性或者方法子类可以直接访问,而对于其中的私有属性可以通过父类暴露出来的某些方法进行调用(如:案例中的 getName() / setName() / getAge() …);
- 子类在创建对象的时候,必然会调用父类的构造函数,假如父类没有提供无参构造器,那么在子类中必须使用super关键字进行指定,不然编译不会通过
public class AA {
private String name;
public AA(){ //当这个方法被注释以后,BB中的无参构造器会报错
System.out.println("这是AA类的无参构造方法(AA是父类)");
}
public AA(String name){
this.name = name;
System.out.println("这是AA类的有参构造方法(AA是父类)");
}
}
public class BB extends AA{
public BB(){ // 当AA中的无参构造被注释以后,该方法会报错
System.out.println("这是BB类的无参构造方法(BB是子类)");
}
// public BB(){ // 针对上一个构造器中存在的问题提出的一个解决方案
// super("张三");
// System.out.println("这是BB类的无参构造方法(BB是子类)");
// }
public BB(String name){
super(name);
// this(); // this()方法和super()方法不可以共存
System.out.println("这是BB类的有参构造方法(BB是子类)");
}
}
public class ExtendsAppTest {
public static void main(String[] args) {
// BB bb = new BB("哈哈哈");
BB bb = new BB();
}
}
运行结果:
其他的情况可以自己测试一下
- 在使用super关键字的时候,必须将其放在第一排;
可以看到这个时候,super哪里已经显示红线了 - java中的继承是单继承(一个子类只能有一个父类),一个父类可以有多个实现类;
- java中的所有类都是Object类的子类;
- super()和this()不能同时存在同一个构造器;
public class ExtendsAppTest02 {
public static void main(String[] args) {
CCC ccc = new CCC();
System.out.println(ccc.name);
System.out.println(ccc.getAge());
System.out.println(ccc.getMoney());
}
}
class AAA{
String name = "AAA --> 爷爷";
private int money = 10000;
public int getMoney(){
return money;
}
}
class BBB extends AAA{
public String name = "BBB --> 爸爸";
private int age = 45;
public int getAge(){
return age;
}
}
class CCC extends BBB{
public String name = "CCC --> 儿子";
}
运行结果:
带有继承关系的类在输出的时候,遵循一定的法则:
①先看当前类中是否有该属性;
②如果当前类有该属性,而且该属性可以被访问,就直接输出;
③如果当前类没有该属性,就会往上找看看父类是否有该属性;
④如果父类有该属性,而且该属性可以被访问,就直接输出;
⑤如果父类没有该属性,则会继续找父类的父类(一直从第③步开始重复一直到Object类);
重写(Override)
重写:就是在子类对父类中的某个方法进行重写;
public class OverriderTest {
public static void main(String[] args) {
Son01 son01 = new Son01();
son01.eat();
System.out.println(son01.getObject());
}
}
class Father01{
public void eat(){
System.out.println("我是父亲,我一顿可以吃三碗");
}
public Father01 getObject(){
return new Father01();
}
}
class Son01 extends Father01{
@Override
public void eat(){
System.out.println("我是儿子,我一顿可以吃一碗");
}
@Override
public Son01 getObject(){
return new Son01();
}
}
运行结果:
注意:
①在子类中被重写的方法,其参数,方法名必须和父类中的方法相同;
②子类的返回值类型和父类一样,或者是父类返回类型的子类
②重写的方法其访问修饰符的范围不能小于父类
重载(Overload)
重载:在java中,允许一个类中出现多个同名的方法,但是要求这些方法的形参列表(形参的个数,形参的类型,形参的顺序)不同
public class OverLoadTest {
public static void main(String[] args) {
Sum sum = new Sum();
System.out.println(sum.add(1,5));
System.out.println(sum.add(1,5,6));
System.out.println(sum.add(1.9,5.1));
}
}
class Sum{
public int add(int a,int b){
return a+b;
}
public int add(int a,int b,int c){
return a+b+c;
}
public double add(double a,double b){
return a+b;
}
// 返回值类型不同,不能看做是否重载的一项依据
// public double add(int a,int b){
// return a+b;
// }
}
运行结果:
注意:
①方法名必须相同;
②形参列表必须不同(形参的个数不同、形参的类型不同、形参的顺序不同);
③返回值类型相同与否与是否重载没有关系;
重写和重载的区别:
两者都是java多态的一种体现。重载是发生在本类中,要求其方法名相同,形参列表不同;重写发生子类和父类中,要求其方法名,形参列表都必须相同,返回值类型也需要父类一样,或者是父类返回类型的子类。
3.多态
多态作为面向对象的三大特性中的一种,是建立在继承和封装上的
所谓多态就是指的是当一个引用变量在程序运行时指定不同的实例对象,会产生不同的结果;
public class PolymorphicTest01 {
public static void main(String[] args) {
Animal animal01 = new Dog();
Animal animal02 = new Cat();
animal01.say();
animal02.say();
}
}
class Animal{
public void say(){
System.out.println("动物发出叫声");
}
}
class Dog extends Animal{
@Override
public void say(){
System.out.println("狗发出叫声");
}
}
class Cat extends Animal{
@Override
public void say(){
System.out.println("猫发出叫声");
}
}
运行结果
在上面的案例中animal01指向的是Dog的实例对象,animal02指向的是Cat的实例对象,在输出时,分别输出了不同的内容,这就是多态的一个体现;
注意:
①一个对象的编译类型和运行类型可以不一致;
②编译类型在定义对象的时候就确定了不可以改变;
③运行类型是可以变化的;
④编译看 = 左边,运行看 = 右边;
⑤多态的前提是两个对象(类)存在继承关系;
向上转型
本质:父类的引用指向了子类的对象;
例如:Animal animal01 = new Dog();
注意:①在访问修饰符允许的情况下,可以调用父类里所有的方法;②不能调用子类特有的成员;
向下转型
本质:引用类型强转
例如:Dog dog = (Dog) animal;
注意:①父类的引用必须指向的是当前目标类型的对象;②可以调用子类中所有的所有的可访问的成员属性和方法;
重载和重写
方法的重载和重写也是属于多态的一种体现(详见上面);
instanceOf 比较操作符
instanceOf 用于判断当前对象是否是某个特定类或者是该特定类的子类的一个实例;
语法:obj instanceof XX(所需判断的类);
public class PolymorphicTest01 {
public static void main(String[] args) {
Animal animal = new Animal();
Animal dog = new Dog();
System.out.println(animal instanceof Animal); // true
System.out.println(animal instanceof Dog); // false
System.out.println(animal instanceof Object); // true
System.out.println(dog instanceof Dog); // true
System.out.println(dog instanceof Animal); // true
System.out.println(null instanceof Object); // false
}
}
注意:①obj 必须为引用类型,不能是基本类型;②obj 为 null:如果 obj 为 null,那么将返回 false;
动态绑定机制
Java的动态绑定又称为运行时绑定。即:程序会在运行的时候自动调用那个方法。
public class PolymorphicTest02 {
public static void main(String[] args) {
A a = new B();
a.say(); // BBBBBBBBBBBB
System.out.println(a.name); // A
}
}
class A{
public String name = "A";
public void say(){
System.out.println("AAAAAAAAAAAA");
}
}
class B extends A{
public String name = "B";
@Override
public void say(){
System.out.println("BBBBBBBBBBBB");
}
}
结论:
- 当调用对象方法的时候,当前方法会和当前调用对象实际内存进行绑定;
- 当调用属性的时候,不存在动态绑定,所以在哪里声明就在那里使用;
二、类方法和类变量
静态变量
类变量:也叫静态属性或者是静态变量,是该类所有的实例共享的一个属性,任何实例对象去访问它的时候都是访问的是同一个属性,当有对象实例对其进行修改的时候,修改的也是同一个实例;
public class StaticTest01 {
public static void main(String[] args) {
A01 a = new A01();
System.out.println(A01.name); // A01
System.out.println(a.name); // A01
a.name = "AA";
System.out.println(A01.name); // AA
System.out.println(a.name); // AA
A01.name = "AAA";
System.out.println(A01.name); // AAA
System.out.println(a.name); // AAA
}
}
class A01{
public static String name = "A01";
}
注意:
- 类变量和普通变量的区别:类变量是该类的所有的对象所共享的,而实例对象是每个对象独有的;
- 类变量是在类加载的时候就初始化了;
- 对类中的类变量进行访问的时候需要遵守访问修饰符的限制;
- 类变量的声明周期是随类的加载开始,随着类的消亡而销毁;
类方法
类方法也叫静态方法,该方法也是所有类成员所共享的;
public class StaticTest02 {
public static void main(String[] args) {
System.out.println(A02.add(1,3)); // 4
A02 a = new A02();
System.out.println(a.add(1,3,5)); // 9
System.out.println(a.add(1,3,5,1)); // 10
}
}
class A02{
private int count = 0;
public static int add(int a,int b){
return a+b;
}
public static int add(int a,int b,int c){
return add(a,b)+c;
}
public int add(int a,int b,int c,int d){
return add(a,b,c)+d;
}
}
注意:
①类方法和类变量都是随着类的加载而加载的;
②类方法中只能访问该类中的静态成员;
③普通方法既可以访问静态成员也可以访问非静态成员;
④类方法中不能够使用与对象有关的this、super等关键字,而普通成员可以;
三、final关键字
在java中final关键字可以用来修饰 类、方法、变量;
public class FinalTest01 {
public static void main(String[] args) {
A04 a = new A04(); //final A04
a.print(); // final 类
A05 a05 = new A05("张三");
a05.print01(); // 这是final方法
System.out.println(a05.getInfo()); // 张三 | 这是final变量
// A05.content = "你好";
}
}
final class A04{
public A04(){
System.out.println("final A04");
}
public void print(){
System.out.println("final 类");
}
}
//class B04 extends A04{
//
//}
class A05{
public final static String content = "这是final变量";
private final String name;
public A05(String name1){
name = name1;
}
public final void print01(){
System.out.println("这是final方法");
}
public final String getInfo(){
return name + " | " +content;
}
}
作用:
- 当使用final来修饰类的时候,表明该类是不可以被继承的;
- 当使用final来修饰方法的时候,表明该方法不可以被子类重写;
- 当使用final来修饰变量的时候,表明该变量不可以被修改;
注意:
- 对于final类型的变量应该在定义时进行赋值,还可以同过构造函数,代码块的方式进行赋值;
- 假如final类型定义的变量是静态变量,就只有定义的时候赋值和通过静态代码块的方式进行赋值;
- final类不可以被继承但可以创建实例对象;
- final方法虽然不可以重写但可以继承;
四、抽象类
在java中使用abstract修饰的类叫做抽象类,使用abstract修饰的方法叫做抽象方法;
public class AbstractTest01 {
public static void main(String[] args) {
Person01 person01 = new Teacher();
Person01 person02 = new Worker();
person01.work(); // 老师上课
person02.work(); // 工人上班
B03 b03 = new B03();
}
}
abstract class Person01{
public abstract void work();
}
class Teacher extends Person01{
@Override
public void work() {
System.out.println("老师上课");
}
}
class Worker extends Person01{
@Override
public void work() {
System.out.println("工人上班");
}
}
abstract class Man extends Person01{
}
abstract class A03{
// public abstract String name = "hhh";// 这个是错的 abstract只能修饰类或者方法
public static String content = "abstract";
private String username;
public void print(){
System.out.println("print");
}
public A03(){
System.out.println("A03");
}
}
class B03 extends A03{
}
注意:
- 抽象类不能够实例化;
- 抽象方法没有具体的方法体;
- 抽象类不一定必须有抽象方法,但是一个类有抽象方法那他一定是一个抽象类;
- abstract只能修饰类或者方法;
- 当一个类继承了继承类抽象类那么这个类就需要实现父类中的抽象方法,而当这个子类也是一个抽象类的时候就不需要实现;
- 抽象类也像普通类一样可以拥有构造器、静态属性、非静态属性、普通方法等;
- 抽象类不能够使用与继承违背的关键字来修饰,如:final;
- 抽象方法不能够使用与重写相违背的关键字,如:static、private、final;
五、代码块
在java中存在着四种代码块:普通代码块、构造代码块、静态代码块、同步代码块;
- 普通代码块:在方法或语句中出现{ }的部分,它的执行由其调用顺序决定的;
- 构造代码块:直接在类中定义没有static带{ }的部分,它在创建对象的时候执行,优于构造器的执行,如果有多个构造代码块,则按其定义的顺序执行;
- 静态代码块:直接在类中使用static{ }定义的部分,它随类的加载(创建对象实例、创建子类对象实例、使用类的静态属性、使用反射进行某个类对象的创建、)而执行,并且只会执行一次,如果类中存在多个静态代码块,则按其定义的顺序执行;
- 同步代码块:使用synchronized(){ }定义的部分,它适用于多线程环境(本次不做演示);
public class CodeBlockTest01 {
public static void main(String[] args) {
A06 a06 = new A06();
a06.print();
System.out.println(" -------------------------------- ");
A06 a061 = new A06();
System.out.println(" -------------------------------- ");
A06 a062 = new A06();
}
}
class A06{
static {
System.out.println("静态代码块01");
}
static {
System.out.println("静态代码块02");
}
{
System.out.println("构造代码块01");
}
{
System.out.println("构造代码块02");
}
public void print(){
System.out.println("这是普通代码块");
}
}
运行结果:
类的加载,静态代码块的执行:
public class CodeBlockTest02 {
public static void main(String[] args) {
// 创建对象
// B06 b06 = new B06(); // 静态代码块01 构造代码块01
// 创建子类对象
// C01 c01 = new C01(); // 静态代码块01 构造代码块01
// 使用静态方法
// B06.print02(); // 静态代码块01 这是静态方法
// 使用静态属性
// 使用静态属性
// B06.name = "张三"; // 静态代码块01
// 反射
try {
Class.forName("com.fei.object_.B06"); //静态代码块01
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class B06{
public static String name;
static {
System.out.println("静态代码块01");
}
{
System.out.println("构造代码块01");
}
public void print01(){
System.out.println("这是普通代码块");
}
public static void print02(){
System.out.println("这是静态方法");
}
}
在创建对象的时候,类中各部分执行的顺序:
public class CodeBlockTest03 {
public static void main(String[] args) {
A07 a07 = new A07();
}
}
class A07{
private static int a1 = getVal01();
static {
System.out.println("静态代码块01");
}
private static int a2 = getVal02();
private int a3 = getVal03();
{
System.out.println("构造代码块01");
}
private int a4 = getVal04();
public static int getVal01(){
System.out.println("getVal01");
return 1;
}
public static int getVal02(){
System.out.println("getVal02");
return 1;
}
public static int getVal03(){
System.out.println("getVal03");
return 1;
}
public static int getVal04(){
System.out.println("getVal04");
return 1;
}
public A07(){
System.out.println("构造方法");
}
}
运行结果:
顺序:
- 静态代码块和静态属性(二者谁先定义,谁先执行);
- 构造代码块和普通属性(二者谁先定义,谁先执行);
- 构造方法;
当子类在创建对象的时候,子类和父类中个部分执行的顺序
public class odeBlockTest04 {
public static void main(String[] args) {
C02 c02 = new C02();
}
}
class B07{
private static int a1 = getVal01();
static {
System.out.println("父类------静态代码块01");
}
private static int a2 = getVal02();
private int a3 = getVal03();
{
System.out.println("父类------构造代码块01");
}
private int a4 = getVal04();
public void print01(){
System.out.println("父类------这是普通代码块");
}
public static void print02(){
System.out.println("父类------这是静态方法");
}
public static int getVal01(){
System.out.println("父类------getVal01");
return 1;
}
public static int getVal02(){
System.out.println("父类------getVal02");
return 1;
}
public static int getVal03(){
System.out.println("父类------getVal03");
return 1;
}
public static int getVal04(){
System.out.println("父类------getVal04");
return 1;
}
public B07(){
System.out.println("父类------构造方法");
}
}
class C02 extends B07{
private static int a1 = getVal01();
static {
System.out.println("子类------静态代码块01");
}
private static int a2 = getVal02();
private int a3 = getVal03();
{
System.out.println("子类------构造代码块01");
}
private int a4 = getVal04();
public void print01(){
System.out.println("子类------这是普通代码块");
}
public static void print02(){
System.out.println("子类------这是静态方法");
}
public static int getVal01(){
System.out.println("子类------getVal01");
return 1;
}
public static int getVal02(){
System.out.println("子类------getVal02");
return 1;
}
public static int getVal03(){
System.out.println("子类------getVal03");
return 1;
}
public static int getVal04(){
System.out.println("子类------getVal04");
return 1;
}
public C02(){
System.out.println("子类------构造方法");
}
}
执行结果:
- 父类的静态代码块和静态属性(二者谁先定义,谁先执行);
- 子类的静态代码块和静态属性(二者谁先定义,谁先执行);
- 父类的构造代码块和普通属性(二者谁先定义,谁先执行);
- 父类的构造方法;
- 子类的构造代码块和普通属性(二者谁先定义,谁先执行);
- 子类的构造方法;
六、接口
是一个抽象类型,是抽象方法的集合;
首先,我们定义一个Usb接口
public interface IUsb {
// 进行连接的方法
public void connection();
// 进行数据传输的方法
public void transmission();
}
这里面有两个方法,对于不同的实现类,两个方法会呈现出不同的效果;
public class Phone implements IUsb {
@Override
public void connection() {
System.out.println("手机连接电脑的usb接口");
}
@Override
public void transmission() {
System.out.println("手机通过usb接口和电脑传输数据");
}
}
public class InterfaceTest01 {
public static void main(String[] args) {
IUsb phoneUSB = new Phone();
IUsb usbDrive = new USBDrive();
phoneUSB.connection(); // 手机连接电脑的usb接口
phoneUSB.transmission(); // 手机通过usb接口和电脑传输数据
usbDrive.connection(); // u盘通过电脑的usb接口进行连接
usbDrive.transmission(); // u盘通过电脑的usb接口进行数据传输
}
}
对于Phone类和USBDriver类,两个不同的类,实现类相同的接口,实现不一样的功能,这就是接口作用的一个体现;
看到这个实例,感觉接口和继承有些相似之处,所以接下来我们来总结一下接口和继承里的不一样
- 二者的修饰符不一样(接口: implements,继承:extends);
- 继承只能够单继承,但是对于接口可以多实现;
public class InterfaceTest02 {
public static void main(String[] args) {
EE01 ee01 = new EE01();
}
}
interface AA01{
public void aPrint();
}
interface BB01{
public void bPrint();
}
interface CC01{
public void cPrint();
}
interface DD01{
public void dPrint();
}
class EE01 implements AA01,BB01,CC01,DD01{
@Override
public void aPrint() {
}
@Override
public void bPrint() {
}
@Override
public void cPrint() {
}
@Override
public void dPrint() {
}
}
- 接口相比继承更加的灵活,源于接口的特性使得它具有更好的拓展性;
关于接口的使用应该要注意的几个地方:
-
接口不可以实例化,但是可以使用接口的引用去指向一个实现了接口的对象
-
一个类可以实现多个接口;
-
接口中抽象方法可以不用编写abstract,只有public 和 默认两种访问权限;
-
接口中的所有的属性都是public final static 的;
-
对于类来说只能是单继承,但是对于接口来说可以多继承(继承只能是继承接口,不能继承类);
interface FF01 extends AA01,BB01,CC01,DD01{
public void fPrint();
}
- 如果一个抽象类去实现接口,那么抽象类可以不同实现其中的方法;
abstract class GG01 implements FF01{
}
- 接口中可以存在某个方法包含其方法体,但其方法的类型必须是static
interface BB01{
public void bPrint();
public static void print4(){
System.out.println("print4");
}
static void print5(){
System.out.println("print5");
}
}
接口和抽象类的区别
- 接口中不存在构造方法,抽象类中有;
- 接口中的访问修饰符只有两种,而抽象类中没有限制;
- 接口可以多继承,抽象类只能单继承;
- 接口中的属性只能是常量,抽象类中没有限制;
- 接口中不存在普通方法的实现,抽象类有;
七、内部类
内部类:一个类中包含了另一个类的结构,这种被包含的类就叫做内部类,而包含的类叫做外部类;
在java中存在着四种内部类(成员内部类、局部内部类、匿名内部类、静态内部类);
成员内部类
public class InnerClassTest01 {
public static void main(String[] args) {
A08 a08 = new A08();
// 通过直接new的方式来创建内部类的实例
A08.B05 b05 = a08.new B05();
b05.print01();
b05.print02();
b05.info();
System.out.println("--------------------------------------------------------");
// 通过 InnerB01 的外部类暴露一个创建 InnerB01 实例对象的方法来进行实例的创建
A08.B05 instance = a08.getInstance();
instance.print02();
instance.print01();
instance.info();
}
}
class A08{
private String name = "A08";
public void show01(){
System.out.println("A08");
}
public void info(){
System.out.println("A08 外部内");
}
public class B05{
private String name = "B05";
private String address = "北京";
public void print01(){
System.out.println(name+" | "+address);
}
public void print02(){
System.out.println(A08.this.name); // 访问外部内中的同名属性
}
public void info(){
A08.this.info(); // 访问外部内中的同名方法
System.out.println("B05 内部类");
}
}
public B05 getInstance(){
return new B05();
}
}
运行结果:
注意:
- 成员内部类也像成员一样有四种访问修饰符(注意使用的时候的搭配);
- 成员内部类中不能存在任何static的变量和方法;
- 成员内部类可以随意访问外部类中的任何成员,不受访问修饰符的限制;
- 成员内部类依附于外部类,就需要通过外部类才能创建内部类的相关实例;
局部内部类
局部内部就是存在于外部类的局部位置,例如:方法;
public class InnerClassTest02 {
public static void main(String[] args) {
A09 a09 = new A09();
a09.inner01(); // A09 : A09 InnerB02 : InnerB02
}
}
class A09{
private String name = "A09";
public String getName(){
return name;
}
public void inner01(){
// 创建了 局部内部类
class InnerB02{
private String content = "InnerB02";
public void printInfo(){
System.out.println("A09 : "+getName());
System.out.println("InnerB02 : "+content);
}
}
// 使用局部内部类
InnerB02 innerB02 = new InnerB02();
innerB02.printInfo();
}
}
注意:
- 不能使用访问修饰符,但是可以使用final进行修饰;
- 可以随意访问外部类中的任何成员,不受访问修饰符的限制;
- 外部类访问局部内部类的时候需要先创建内部类的实例对象,在调用方法;
- 局部内部类相当于一个局部变量,其他的内部类也不能访问;
- 局部内部类访问与外部类重名方法时,访问方式和成员内部类相同;
匿名内部类
public class InnerClassTest03 {
public static void main(String[] args) {
A10 a10 = new A10();
a10.b08.show01();
a10.b08.show02(10);
a10.b08.show03();
B09 b = new B09(){
@Override
public void print02() {
System.out.println("abstract print02");
print03();
}
public void print03(){
System.out.println("我是新增的");
}
};
b.print02(); // abstract print02 我是新增的
}
}
class A10{
private String name = "A10";
public String getName() {
return name;
}
interface B08{
public void show01();
public void show02(int x);
public void show03();
}
B08 b08 = new B08() {
class C01{
public String address = "北京";
}
private String name = "B08";
public static final String STR = "hello b08";
@Override
public void show01() {
System.out.println("匿名内部类 show01");
}
@Override
public void show02(int x) {
System.out.println("匿名内部类 show02 | " + x);
}
public void show03(){
C01 c01 = new C01();
System.out.println(c01.address);
}
public void show04(){
System.out.println("A01.name = " + getName());
System.out.println("b08.name = " + name);
System.out.println("STR = " + STR);
show03();
}
// 匿名内部类中不能定义静态属性、方法
// public static String s = "hello world"; // 错误
// public static void show04(){ // 错误
// System.out.println("static");
// }
};
}
abstract class B09{
public void print01(){
System.out.println("print01");
}
public abstract void print02();
}
运行结果:
注意:
- 匿名内部类没有类名;
- 匿名内部类可以随意访问外部类中的任何成员,不受访问修饰符的限制;
- 匿名内部类访问与外部类重名方法时,访问方式和成员内部类相同;
- 匿名内部类也可以添加属性、方法(父类中没有的)、内部类、对其他类进行实例化;
- 匿名内部类中不能定义静态属性、方法但可以拥有final类型的属性;
静态内部类
public class InnerClassTest04 {
public static void main(String[] args) {
A11.B10 b10 = new A11.B10();
b10.print01(); // B10 print01
A11.B10.print02(); // B10 static print02
A11.B10.print03(); // A11 static show02
System.out.println(A11.B10.address); // B10 static
}
}
class A11{
private String name = "A11";
private static String information = "A11 static";
public void show01(){
System.out.println("A11 name= " +name);
}
public static void show02(){
System.out.println("A11 static show02");
}
static class B10{
private String name = "B10";
public static String address = "B10 static";
// static A11 a = new A11();
public void print01(){
System.out.println("B10 print01");
}
public static void print02(){
System.out.println("B10 static print02");
}
public static void print03(){
// show01(); // 访问不了
show02();
}
public void print04(){
// show01(); // 访问不了
show02();
}
}
}
注意:
- 静态内部类只能够访问外部类的静态成员,不受访问修饰符的限制(若要访问非静态的需要创建外部类的对象);
- 若要访问外部类同名的成员,需要使用外部类名.成员名字的方式;
- 静态内部类也受四种访问修饰符的限制;