1. 继承
(1)把多个类中相同的属性和行为提取出来,定义到一个类中,然后让这多个类和这一个类产生一个关系,这多个类就具备这一个类的属性和行为了。这种关系叫:继承。
(2)继承如何表示的呢?
Java语言中类的继承是通过extends关键字来实现的,其格式如下:
class 子类名 extends 父类名 {}
class SubClass extends SuperClass { … }
子类可以从父类那里继承所有非private的成员作为自己的成员。
若无extends关键字,则该类默认为java.lang.Object类的子类。
子类的每个对象也是父类的对象(“即是”性质),但父类对象不一定是子类的对象。
class 人类{ ... }
class 男人类 extends 人类{ ... }
class 女人类 extends 人类{ ... }
使用继承过来的成员时,可利用“子类对象.父类成员名”格式进行。
在执行子类的构造方法之前,将先调用父类中没有参数的构造方法,其目的是为了帮助继承自父类的成员做初始化的操作。
严格意义上说,构造方法是不能被继承的,但子类可以调用父类的构造方法。
- 测试demo
class Person {
public String name;
public int age;
public Person() {
System.out.println("public Person()");
}
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("public Person(String name, int age)");
}
public void show() {
System.out.println("name:" + name + ", age:" + age);
}
}
class Man extends Person {
public int sex;
public Man() {
this.sex = 0;
}
public Man(String name, int age) {
this.name = name;
this.age = age;
this.sex = 0;
}
}
class Woman extends Person {
private int sex;
public Woman() {
this.sex = 1;
}
public Woman(String name, int age) {
this.name = name;
this.age = age;
this.sex = 1;
}
}
public class App {
public static void main(String[] args) {
Person p = new Person("AAA", 11);
p.show();
Man m1 = new Man("BBB", 12);
m1.show();
Woman m2 = new Woman("CCC", 13);
m2.show();
}
}
(3)java中的继承特点:
通过类的继承方式,可以不用编写相同的代码就能开发出新的类,即程序代码复用。
子类继承父类的成员变量和方法,同时还可修改、重写和添加父类的成员变量和方法。
被继承的类称为父类、基类或超类(superclass),由继承而得到的类称为子类(subclass)或者派生类。
Java语言中不支持多重继承,所以一个类只能有一个直接父类。
class SubDemo extends Demo{} //ok
class SubDemo extends Demo1,Demo2…//error
java中可以多层继承。(继承体系)
class A{}
class B extends A{}
class C extends B{}
Java语言中有一个名为java.lang.Object的特殊类,所有的类都是直接或间接地继承该类而得到的。
class A {
public int data_a = 1;
}
class B extends A {
public int data_b = 2;
}
class C extends B {
public int data_c = 3;
}
public class App {
public static void main(String[] args) {
A a = new A();
System.out.println("a.data_a = " + a.data_a);
System.out.println("---------------------");
B b = new B();
System.out.println("b.data_a = " + b.data_a);
System.out.println("b.data_b = " + b.data_b);
System.out.println("---------------------");
C c = new C();
System.out.println("c.data_a = " + c.data_a);
System.out.println("c.data_b = " + c.data_b);
System.out.println("c.data_c = " + c.data_c);
System.out.println("---------------------");
}
}
(4)java中的继承的好处和弊端
好处:提高了代码的复用性;提高了代码的可维护性;让类与类之间产生了一个关系,是多态的前提。
弊端:让类与类的耦合增强了,这样一个类的改动会直接影响另一个类。
设计原则:高内聚低耦合。
简单的理解:
内聚就是自己完成某件事情的能力。
耦合就是类与类之间的关系。
我们在设计的时候原则是:自己能完成的就不麻烦别人,这样将来别人产生了修改,就对我的影响较小。
由此可见:在开发中使用继承其实是在使用一把双刃剑。
(5)java中的继承的注意事项:
A:私有成员不能被继承
B:构造方法不能被继承,想访问,通过super关键字
C:不能为了部分功能而去使用继承
(6)继承中的成员关系:
成员变量与成员方法
不同名:特别简单,一看就知道用的是谁。
同名:就近原则
访问自己的用this,访问父亲的用super
构造方法
子类的所有构造方法默认都是访问父类的无参构造方法
如果父类没有无参构造方法,怎么办呢? /提问:什么时候没有无参构造?
通过super(…)访问父类带参构造方法
通过this(…)访问本类其他构造方法。(一定要有一个访问了父类的构造方法)
注意:super或者this只能出现一个,并且只能在语句的第一条语句。
为什么呢?
因为子类可能会访问父类的数据,所以,在子类初始化之前,要先把父类数据初始化完毕。
(7)this和super的区别及应用场景
区别
this:本类对象的引用
super:父类存储空间的标识。可以理解为父类对象的引用。
应用场景
成员变量
this.变量 ----- 本类的成员变量
super.变量 ----- 父类的成员变量
构造方法
this(…) ----- 本类的构造方法
super(…) ----- 父类的构造方法
成员方法
this.方法名(…) ---- 本类的成员方法
super.方法名(…) ----父类的成员方法
2. super关键字
- 在父类中定义一个成员变量
- 在子类中定义一个成员变量和父类中成员变量名称不同,然后再在子类中定义一个方法去访问变量,发现变量名不同,访问非常简单
- 在子类中再定义一个成员变量,和父类中的成员变量名称一致,然后继续访问。发现访问的是子类的成员变量。
- 如果我要访问父类的成员变量该怎么办呢? 通过回想 this 来引入 super 关键字
super的用法和this很像
this代表本类对应的引用。
super代表父类存储空间的标识(可以理解为父类引用)
用法(this和super均可如下使用)
访问成员变量
this.成员变量
super.成员变量
访问构造方法
this(…)
super(…)
访问成员方法
this.成员方法()
super.成员方法()
继承中构造方法的关系:子类中所有的构造方法默认都会访问父类中空参数的构造方法
为什么呢?
因为子类会继承父类中的数据,可能还会使用父类的数据;
所以,子类初始化之前,一定要先完成父类数据的初始化。
每一个构造方法的第一条语句默认都是:super()
- demo
class Person {
public String name;
public int age;
public Person() {
System.out.println("public Person()");
}
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("public Person(String name, int age)");
}
public void show() {
System.out.println("name:" + name + ", age:" + age);
}
}
class Man extends Person {
public int sex;
public Man() {
this.sex = 0;
}
public Man(String name, int age) {
super(name, age);
this.sex = 0;
}
}
class Woman extends Person {
private int sex;
public Woman() {
this.sex = 1;
}
public Woman(String name, int age) {
super(name, age);
this.sex = 1;
}
}
public class App {
public static void main(String[] args) {
Person p = new Person("AAA", 11);
p.show();
Man m1 = new Man("BBB", 12);
m1.show();
Woman m2 = new Woman("CCC", 13);
m2.show();
}
}
3. final关键字
final:最终的意思。
作用:可以修饰类,成员变量,成员方法。
特点:
类:类被final修饰,说明该类是最终类,不能被继承。
成员变量:变量被final修饰后,就变成了常量。值不能被修改。
成员方法:方法不能被子类重写。
final修饰局部变量
基本类型:基本类型的值不能变量
引用类型:引用类型的地址值不能改变
final的初始化时机:
A:在定义的时候立即给值。以后在也不能改了。
B:在定义后,构造方法结束前赋值都可以。
final class A {
public int data_1 = 1;
public int data_2 = 2;
}
class B {
public int data_3 = 3;
public int data_4 = 4;
final public int data_5 = 5;
final public void show() {
System.out.println("data_3 = " + this.data_3);
System.out.println("data_4 = " + this.data_4);
System.out.println("data_5 = " + this.data_5);
}
}
class C extends B {
public int data_6 = 6;
// public void show() { } // error
}
// class B extends A { } // error
public class App {
public static void main(String[] args) {
C c = new C();
c.show();
// c.data_5 = 15;// error
System.out.println("data_6 = " + c.data_6);
}
}
面试题:
final修饰局部变量
a:基本类型 ----- 值不能发生改变
b:引用类型 ----- 地址值不能发送改变,对象的内容是可以改变的
final的初始化时机
a:在定义时就赋值
b:在构造方法完毕前赋值
4. 方法重写/override
(1)描述:在子类中,出现了和父类中一模一样的方法声明的现象。
(2)作用:可以使用父类功能,还可以增强该功能。
(3)方法重写的注意事项:
- 父类私有方法不能被重写
- 子类重写方法的访问权限不能比父类的方法低
- demo
class Person {
public String name;
public int age;
public Person() {
System.out.println("public Person()");
}
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("public Person(String name, int age)");
}
public void show() {
System.out.println("name:" + name + ", age:" + age);
}
}
class Man extends Person {
public int sex;
public Man() {
this.sex = 0;
}
public Man(String name, int age) {
super(name, age);
this.sex = 0;
}
public void show() {// override
System.out.println("name:" + name + ", age:" + age + ", sex:" + sex);
}
}
class Woman extends Person {
private int sex;
public Woman() {
this.sex = 1;
}
public Woman(String name, int age) {
super(name, age);
this.sex = 1;
}
public void show() { // override
System.out.println("name:" + name + ", age:" + age + ", sex:" + sex);
}
}
public class App {
public static void main(String[] args) {
Person p = new Person("AAA", 11);
p.show();
Man m1 = new Man("BBB", 12);
m1.show();
Woman m2 = new Woman("CCC", 13);
m2.show();
}
}
(4)面试题:
重写override和重载overload的区别?
方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的参数列表,有兼容的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求,不能根据返回类型进行区分。
重写(覆盖)的规则:
1、重写方法的参数列表必须完全与被重写的方法的相同,否则不能称其为重写而是重载.
2、重写方法的访问修饰符一定要大于被重写方法的访问修饰符(public>protected>default>private)。
3、重写的方法的返回值必须和被重写的方法的返回一致或者兼容;
4、重写的方法所抛出的异常必须和被重写方法的所抛出的异常一致,或者是其子类;
5、被重写的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行重写;
6、静态方法不能被重写为非静态的方法(会编译出错);
7、父类方法被final时,无论该方法被public、protected及默认所修饰,子类均不能重写该方法。
overload是重载,一般是用于在一个类内实现若干重载的方法,这些方法的名称相同而参数形式不同。
重载的规则:
1、在使用重载时只能通过相同的方法名、不同的参数形式实现。不同的参数类型可以是不同的参数类型,不同的参数个数,不同的参数顺序(参数类型必须不一样);
2、不能通过访问权限、返回类型、抛出的异常进行重载;
3、方法的异常类型和数目不会对重载造成影响。
5. 多态
(1)多态:即多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。
(2)多态的前提:有继承关系、有方法重写、有父类引用指向子类对象
多态的产生:多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。
(3)多态中的成员访问特点:
成员变量:编译看左边,运行看左边
成员方法:编译看左边,运行看右边
静态方法:编译看左边,运行看左边
为什么:因为方法有重写,而变量没有,静态方法没有重写一说。
举例:
class Animal {
public int age = 40;
public void show() {
System.out.println("show animal");
}
public static void method() {
System.out.println("method animal");
}
}
class Dog extends Animal {
public int age = 20;
public void show() {
System.out.println("show dog");
}
public static void method() {
System.out.println("method dog");
}
}
class App {
public static void main(String[] args) {
Animal a = new Dog();
System.out.println(a.age);
a.show();
a.method();
}
}
输出结果
40 ---- 成员变量:编译看左边,运行看左边
show dog ---- 成员方法:编译看左边,运行看右边
method animal ---- 静态方法:编译看左边,运行看左边
多态的好处
提高了程序的维护性(由继承保证)
提高了程序的扩展性(由多态保证)
多态的弊端:不能访问子类特有功能
那么我们如何才能访问子类的特有功能呢?(多态中的转型)
多态中的转型问题
向上转型:从子到父,父类引用指向子类对象
向下转型:从父到子,父类引用转为子类对象
class Animal {
public String name;
public int age;
public Animal() {}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println("Animal.eat()");
}
public void call() {
System.out.println("Animall.call()");
}
}
class Dog extends Animal {
public Dog() {}
public Dog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("Dog.eat()");
}
@Override
public void call() {
System.out.println("Dog.call()");
}
public void lookDoor() {
System.out.println("Dog.lookDoor: " + name);
}
}
class Cat extends Animal {
public Cat() {}
public Cat(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("Cat.eat()");
}
@Override
public void call() {
System.out.println("Cat.call()");
}
}
public class App {
public static void main(String[] args) {
Animal animal = new Dog("aaa", 11);
Cat cat = new Cat("Cat", 12);
Dog dog = new Dog("Dog", 13);
Dog dog2 = (Dog) animal;
animal.call(); // Dog.call()
cat.call(); // Cat.call()
dog.call(); // Dog.call()
dog.lookDoor(); // Dog.lookDoor: Dog
dog2.lookDoor();// Dog.lookDoor: aaa
call(animal); // Dog.call()
call(cat); // Cat.call()
call(dog); // Dog.call()
call(dog2); // Dog.call()
}
private static void call(Animal a) {
a.call();
}
}
6. 抽象类
在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类。
抽象类特点:抽象类和抽象方法必须用abstract关键字修饰
格式
abstract class 类名 {}
public abstract void eat();
抽象类不一定有抽象方法,有抽象方法的类一定是抽象类
抽象类不能实例化
那么,抽象类如何实例化呢?
按照多态的方式,由具体的子类实例化,其实这也是多态的一种,抽象类多态。
抽象类的子类:要么是抽象类,要么重写抽象类中的所有抽象方法
成员变量:可以是变量,也可以是常量
构造方法:有构造方法,但是不能实例化
那么,构造方法的作用是什么呢?
用于子类访问父类数据的初始化
成员方法
可以有抽象方法,限定子类必须完成某些动作
也可以有非抽象方法,提高代码复用性
- 抽象类:demo1
public class App {
public static void main(String[] args) {
// A a = new A(); // error -- 抽象类妄想实例化对象,不可行
// B b = new B(); // error
C c = new C(); // ok
c.eat();
c.call();
c.show();
}
}
abstract class A{ // 抽象类
public abstract void eat(); // 抽象方法
public abstract void call();
public void show() {
System.out.println("A.show()");
}
}
abstract class B extends A{ } // 抽象类继承抽象类,还是抽象的
class C extends A{ // 子类继承抽象类,并重写所有抽象方法 --- 可用来实例化对象
@Override
public void eat() {
System.out.println("C.eat()");
}
@Override
public void call() {
System.out.println("C.call()");
}
}
- 抽象类demo2
abstract class Animal {
public String name;
public int age;
public Animal() {}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public void show() {
System.out.println("name:" + name + ", age:" + age);
}
}
class Cat extends Animal {
public Cat() {}
public Cat(String name, int age) {
super(name, age);
}
@Override
public void show() {
System.out.println("Cat.show()");
super.show();
}
}
public class App {
public static void main(String[] args) {
// Animal animal = new Animal("AAA", 11);// error
Cat cat = new Cat("BBB", 12);
cat.show();
}
}
- 抽象方法:demo
子类要实现父类的抽象方法
abstract class Animal {
public String name;
public int age;
public Animal() {}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
abstract public void show(); // 抽象方法
}
class Cat extends Animal {
public Cat() {}
public Cat(String name, int age) {
super(name, age);
}
public void show() { // 实现抽象方法
System.out.println("Cat.show()");
}
}
public class App {
public static void main(String[] args) {
// Animal animal = new Animal("AAA", 11);// error
Cat cat = new Cat("BBB", 12);
cat.show();
}
}
三个小问题:
-
抽象类的非抽象方法有什么用?抽象方法有什么用?
抽象类的抽象方法:用于限定子类必须完成某些动作
抽象类的非抽象方法:用于提高代码的复用性 -
一个类如果没有抽象方法,可不可以定义为抽象类?如果可以,有什么意义?
可以。为了不让别人创建对象。 -
abstract不能和哪些关键字共存
final:final修饰的方法不能被重写,而abstract修饰的方法要求必须被重写。
private:private修饰的方法不能被重写,而abstract修饰的方法要求必须被重写。
static:static修饰的方法可以通过类名调用,而abstract修饰的方法是没有方法体的,调用一个没有方法体的方法是没有任何意义的,所以不能共存。
如下举例:
abstract class Student {
public final abstract void show1(); //error
private abstract void show2(); //error
public static abstract void show3();//error
}
7. 接口
为了体现事物功能的扩展性,Java中就提供了接口来定义这些额外功能,并不给出具体实现,将来需要的时候,只需要把这些额外功能实现即可。
接口特点:接口用关键字interface表示
格式:interface 接口名 {}
类实现接口用implements表示
格式:class 类名 implements 接口名 {}
接口不能实例化,那么,接口如何实例化呢?
按照多态的方式,由具体的子类实例化。其实这也是多态的一种,接口多态。
interface Animal {
public void eat();
public void call();
}
class Cat implements Animal {
@Override
public void eat() {
System.out.println("Cat.eat()");
}
@Override
public void call() {
System.out.println("Cat.call()");
}
}
interface Dog extends Animal { // 接口也可以继承
public void lookDoor();
}
public class App {
public static void main(String[] args) {
Cat cat = new Cat();
cat.eat();
cat.call();
}
}
接口的子类:要么是抽象类;要么重写接口中的所有抽象方法
接口成员变量:只能是常量;默认修饰符 public static final
构造方法:没有,因为接口主要是扩展功能的,而没有具体存在
成员方法:只能是抽象方法;默认修饰符 public abstract
接口可以使用多继承
interface A {
public void showA();
}
interface B {
public void showB();
}
interface C extends A, B { // 接口多继承
public void showC();
}
class D implements C {
@Override
public void showA() {
System.out.println("showA()");
}
@Override
public void showB() {
System.out.println("showB()");
}
// Java SE5新增加@Override注解,不是关键字,当覆写(重写)某个方法时,可以选择添加或不添加这个注解
@Override
public void showC() {
System.out.println("showC()");
}
public void showD() {
System.out.println("showD()");
}
}
public class App {
public static void main(String[] args) {
D a = new D();
a.showA();
a.showB();
a.showC();
a.showD();
}
}
类和接口的关系:
类与类:继承关系,只能单继承,但是可以多层继承
类与接口:实现关系,可以单实现,也可以多实现。还可以在继承一个类的同时实现多个接口
接口与接口:继承关系,可以单继承,也可以多继承
成员区别
抽象类:变量,常量;有抽象方法;抽象方法,非抽象方法
接口:常量;抽象方法
关系区别
类与类:继承,单继承
类与接口:实现,单实现,多实现
接口与接口:继承,单继承,多继承
设计理念区别
抽象类:被继承体现的是:"is a"的关系。共性功能
接口:被实现体现的是:"like a"的关系。扩展功能
形式参数:基本类型、引用类型
返回值类型:基本类型、引用类型
抽象类和接口类型作为形式参数。
形式参数
类:需要的是该类的对象
抽象类:需要的是该抽象类的子类对象
接口:需要的是接口的子类对象
具体类作为形式参数:需要的是该类的对象
class Student {
public void show() {
System.out.println("Student.show");
}
}
class StudentDemo {
public void method(Student s) {
s.show();
}
}
class App {
public static void main(String[] args) {
StudentDemo sd = new StudentDemo();
Student s = new Student();
sd.method(s);
}
}
抽象类作为形式参数:需要的是该抽象类的子类对象(抽象类多态)
abstract class Person {
public abstract void show();
}
class Student extends Person {
public void show() {
System.out.println("Student.show");
}
}
class PersonDemo {
public void method(Person p) {
p.show();
}
}
class App {
public static void main(String[] args) {
PersonDemo pd = new PersonDemo();
Person p = new Student();
pd.method(p);
}
}
接口作为形式参数:需要的是该接口的实现类对象 (接口多态)
interface Animall {
public abstract void call();
}
class Cat implements Animall {
@Override
public void call() {
System.out.println("Cat.call()");
}
}
class Demo {
public void method(Animall animall) {
animall.call();
}
}
public class App {
public static void main(String[] args) {
Demo demo = new Demo();
Animall animall = new Cat();
demo.method(animall);
}
}
返回值的问题
基本类型:返回什么就用什么接收。
引用类型
类:其实返回的是该类的对象
抽象类:其实返回的是该类的子类对象
接口:其实返回的是该接口的子类对象
具体类作为返回值类型:返回的是该类的对象
class Student {
public void show() {
System.out.println("Student.show()");
}
}
class StudentDemo {
public Student getStudent() {
// Student s = new Student();
// return s;
return new Student();
}
}
class App {
public static void main(String[] args) {
// 如何测试呢?
// 原本我可以直接通过Student创建对象,调用功能
// 但是现在不让这样做,怎么办呢?
StudentDemo sd = new StudentDemo();
Student s = sd.getStudent();
s.show();
}
}
抽象类作为返回值类型:返回的是该类的子类对象
abstract class Person {
public abstract void show();
}
class Student extends Person {
public void show() {
System.out.println("Student.show()");
}
}
class PersonDemo {
public Person getPerson() {
// Person p = new Student();
// return p;
return new Student();
}
}
class App {
public static void main(String[] args) {
PersonDemo pd = new PersonDemo();
Person p = pd.getPerson();
p.show();
}
}
接口作为返回值类型:返回的是接口的实现类对象
interface Person {
public void show();
}
class Student implements Person {
public void show() {
System.out.println("Student.show()");
}
}
class PersonDemo {
public Person getPerson() {
// Person p = new Student();
// return p;
return new Student();
}
}
class App {
public static void main(String[] args) {
PersonDemo pd = new PersonDemo();
Person p = pd.getPerson();
p.show();
}
}
链式编程的案例演示
new PersonDemo().getPerson().show();
8. 包
包,其实就是文件夹
作用:对类进行分类管理
包的划分(举例):
学生的增加,删除,修改,查询
老师的增加,删除,修改,查询
以及以后可能出现的其他的类的增加,删除,修改,查询
划分依据:按照模块和功能分。
定义包的格式
package 包名;
多级包用.分开即可
注意事项:
package语句必须是程序的第一条可执行的代码
package语句在一个java文件中只能有一个
如果没有package,默认表示无包名
带包的类的编译和运行
手动式
a:javac编译当前类文件。
b:手动建立包对应的文件夹。
c:把a步骤的class文件放到b步骤的最终文件夹下。
d:通过java命令执行。注意了:需要带包名称的执行
java cn.test.HelloWorld
自动式
a:javac编译的时候带上-d即可
javac -d . HelloWorld.java
b:通过java命令执行。和手动式一样
导包
每次使用不同包下的类的时候,都需要加包的全路径,比较麻烦。
这个时候,java就提供了导包的功能。
导包格式:import 包名;
注意:
这种方式导入是到类的名称,虽然可以最后写*,但是不建议。
package,import,class有没有顺序关系(面试题)
权限修饰符(1代表可访问)
类型 | public | protected | 默认 | private |
---|---|---|---|---|
同一类中 | 1 | 1 | 1 | 1 |
同一包子类,其他类 | 1 | 1 | 1 | |
不同包,子类 | 1 | 1 | ||
不同包,其他类 | 1 |
类及其组成可以用的修饰符
类:默认,public,final,abstract;自己定义:public居多
成员变量:四种权限修饰符均可,final,static;我们自己定义:private居多
构造方法:四种权限修饰符均可,其他不可;我们自己定义:public 居多
成员方法:四种权限修饰符均可,fianl,static,abstract;我们自己定义:public居多
9. 内部类
把类定义在其他类的内部,这个类就被称为内部类。
举例:在类A中定义了一个类B,类B就是内部类。
内部类的访问特点:
内部类可以直接访问外部类的成员,包括私有。
外部类要访问内部类的成员,必须创建对象。
内部类位置
按照内部类在类中定义的位置不同,可以分为如下两种格式:
成员位置(成员内部类)
局部位置(局部内部类)
外界如何创建对象
外部类名.内部类名 对象名 = 外部类对象.内部类对象;
package com.baidu;
public class A {
public int a = 1;
int b = 1;
protected int c = 1;
private int d = 1;
public static void main(String[] args) {
show();
A.B b= new A().new B();
b.show();
}
class B{
public void show() {
System.out.println("A:a="+a+", b="+b+", c="+c+", d="+d);
System.out.println("A.B.show()");
}
}
public static void show() {
System.out.println("A.show()");
}
}
一般来说,在实际开发中是不会这样使用的,因为一般内部类就是不让外界直接访问的。
成员内部的常见修饰符
private 为了保证数据的安全性
static 为了让数据访问更方便
被静态修饰的成员内部类只能访问外部类的静态成员
内部类被静态修饰后的方法
静态方法
非静态方法
1:非静态的成员内部类,成员只能是非静态的。
2:内部类被静态修饰后的方法有静态和非静态之分。他们的访问和不用静态是不一样的。
访问非静态方法:外部类名.内部类名 对象名 = new 外部类名.内部类名();
访问静态方法:上面创建的对象访问,或者外部类名.内部类名.方法名();
局部内部类
可以直接访问外部类的成员
可以创建内部类对象,通过对象调用内部类方法,来使用局部内部类功能
局部内部类访问局部变量的注意事项:
必须被final修饰? 为什么呢?
因为局部变量会随着方法的调用完毕而消失,这个时候,
局部对象并没有立马从堆内存中消失,还要使用那个变量。
为了让数据还能继续被使用,就用fianl修饰,这样在堆内存里面存储的其实是一个常量值。通过反编译工具可以看一下。
匿名内部类
就是内部类的简化写法。
前提:存在一个类或者接口
这里的类可以是具体类也可以是抽象类。
格式:new 类名或者接口名() {重写方法;}
本质:是一个继承了类或者实现了接口的子类匿名对象
1:通过new去分析,结果是一个对象。
2:如果类是抽象类,或者new后面是接口,所以整体应该是子类对象。
3:又由于没有名字,所以,整体是一个匿名对象。
加入方法有多个,如何调用呢?
方式1:每一种格式调用一个,太麻烦
方式2:用类或者接口接收该子类对象,多态思想
面试题
1.Override 和Overload 的区别? Overload 能改变返回值类型吗?
Override:重写
Overload:重载
方法重写:子类中出现和父类中一模一样的方法声明的现象。
方法重载:同一个类中出现的方法名相同,参数列表不同的现象。
Overload能改变返回值类型,因为它和返回值类型无关。
2.this 关键字和 super 关键字分别代表什么? 以及他们各自的使用场景和作用。
this:本类对象的引用
super:父类存储空间的标识。(可以理解为父类对象的引用)
使用场景:
成员变量:
this.变量 本类的成员变量
super.变量 父类的成员变量
构造方法:
this(…) 本类的构造方法
super(…) 父类的构造方法
成员方法:
this.方法名(…) 本类的成员方法
super.方法名(…) 父类的成员方法
3.final关键字在修饰变量的时候有什么特性
final修饰局部变量:
在方法内部,该变量不可以被改变
在方法声明上,分别演示基本类型和引用类型作为参数的情况
基本类型,是值不能被改变
引用类型,是地址值不能被改变
final修饰变量的初始化时机:
在对象构造完毕前即可
4.看程序写结果(先判断有没有问题,如果没有,写出结果)
4.1 父类引用指向子类对象,调用子类存在,但父类不存在的方法
class Fu{
public void show(){
System.out.println("fu show");
}
}
class Zi extends Fu{
public void show(){
System.out.println("zi show");
}
public void method(){
System.out.println("zi method");
}
}
class Test{
public static void main(String[] args){
Fu f = new Zi();
f.method();
}
}
4.2 充满爱情的苦
class A {
public void show() {
show2();
}
public void show2() {
System.out.println("我");
}
}
class B extends A {
public void show2() {
System.out.println("爱");
}
}
class C extends B {
public void show() {
super.show();
}
public void show2() {
System.out.println("你");
}
}
public class App {
public static void main(String[] args) {
A a = new A();
a.show();
A b = new B();
b.show();
B c = new C();
c.show();
}
}
5.按照要求,补齐代码
要求:在控制台输出”HelloWorld”
interface Inter { void show(); }
class Outer { //补齐代码 }
class OuterDemo {
public static void main(String[] args) {
Outer.method().show();
}
}
参考答案如下:
interface Inter { void show(); }
class Outer {
//补齐代码
public static Inter method() {
return new Inter() {
public void show() {
System.out.println("HelloWorld");
}
};
}
}
class OuterDemo {
public static void main(String[] args) {
Outer.method().show();
/*
Outer.method():类名能够直接调用方法,说明该方法是静态的。
Outer.method().show():这种用法叫链式编程,调用show()方法之前的内容应该是一个对象
*/
}
}