内部类
概念:
在一个类的类中定义一个类,在一个A类的内部定义一个内部类B,B叫做内部类,A叫做外部类。
内部类的作用:
- 可以无条件的访问外部类的所有元素
- 可以吧内部类通过private将内部类的信息隐藏
- 可以实现多继承(当一个类里面于存在多个内部类的时候,可以让内部类继承别的类,这样就变向的实现了多继承)
访问特点:
内部类访问外部类的内容,直接可以访问,包括私有的。外部类要想访问内部类的内容,必须创建内部类的对象,通过对象来访问。
代码:
public class OtherText {//外部类
int a=10;
private int age=12;
class Inter{//内部类
int a=12;
public void show(){
System.out.println(age);
}
public void method(){
int a=15;
System.out.println(a);
System.out.println(age);
System.out.println(this.a);
System.out.println(OtherText.this.a);
// System.out.println(this.age);
}
}
public void show1(){//成员方法
Inter inter=new Inter();
// inter.xixi;//inter对象里没有xixi变量
}
public void function(){//成员方法
int xixi=15;
}
classText{
public static void main(String[] args) {
OtherText.Inter inter=new OtherText().new Inter();
inter.show();
}
}
}
成员内部类(在类的成员位置)
1,内部类创建对象的理解
public class OtherText {//外部类
int a=10;
private int age=12;
class Inter{//内部类
int a=12;
public void show(){
System.out.println(age);
}
}
}
classText{
public static void main(String[] args) {
OtherText.Inter inter=new OtherText().new Inter();
}
}
//这里这样创建对象是因为直接写Inner找不到Inner在哪里。Inter是在OtheText里面的,所以你的通过OtherText找到Inner,而这里new OtherText().new Inter();因为Inner是OtherText的成员,你想有Inner对象,就要先new Outer的对象。因为Inner本身也是一个类,所以也要通过new 。
2,静态成员内部类
public class Outer {
int a = 10;
public static class Inner{
public void method(){
System.out.println("内部类的method方法");
}
}
}
class Test {
public static void main(String[] args) {
Outer.Inner inner=new Outer.Inner();
/*
Inner 是属于 Outer的静态内容。
静态的随着类的存在而存在的, 所以可以用类名调用。
*/
}
}
3,成员内部类访问当前外部类对象的内容
public class Outer {
int a = 10;
public class Inner{
int a = 20;
public void method(){
int a = 30;
System.out.println(a); // 30
System.out.println(this.a); // 20
//System.out.println(super.a); //编译报错
//System.out.println(Outer.a); //编译报错
System.out.println(Outer.this.a); // 5 这里表示当前内部对象所在的外部对象。
System.out.println(new Outer().a); //10
}
}
int b = 40;
}
局部内部类(在类的局部位置)
1,局部内部类如何把对象扔到外界使用:
思路:如何才能将一个局部内部类扔到外面,让外部可以直接访问呢,我们就可以通过方法的返回值吧对象扔到外界使用,但是Inner是内部的东西,不能直接写,这时候我们就可以利用多态的思想,创建一个接口,让Inner实现这个接口。然后返回值为父类。
interface Face{
void method();
}
class OuterText {
private int num=10;
//成员方法
public Face show(){
class Inner implements Face{
public void method(){
System.out.println(num);
}
}
return new Inner();
}
}
class Test {
public static void main(String[] args) {
//把Inner 扔到这里来用
OuterText o=new OuterText();
Face f = o.show();
f.method();
}
}
2,局部内部类面试题
局部内部类访问局部变量, 局部变量前面必须有final修饰, 在jdk1.8的时候,这个final可以省略,但其实并不是真正的省略,而是 前面默认有一个 final ,只是你看不到而已。
interface Digests{
void diges();
}
public class Person {
public Digests digestion(){
int a = 10;
//a = 20;
class Tract implements Digests{
public void diges(){
System.out.println(a);
}
}
Tract t = new Tract();
return t;
}
}
我们通过画图来了解带fianl的好处:
不带final
带final
通过内存图可以看出,final关键字延长了局部变量的生命周期
匿名内部类
匿名内部类的由来————>
interface Digests{
void diges();
}
class Text{
public static void main(String[] args){
//如何才能调用diges方法
}
}
通过上述代码,我们如何调用diges()方法呢?
- 方案1:创建一个Digests接口的实现类,重写diges()方法,这样有点麻烦
- 方案2:让测试类实现Digests接口,在测试类里重写diges()方法,这样也有点麻烦。
- 方案3:通过匿名内部类,这个类没有名字.
看代码
interface Digests{
void diges();
}
class Text {
public static void main(String[] args){
//如何才能调用diges方法
Digests d=new Digests() {
@Override
public void diges() {
System.out.println("匿名内部类......");
}
};
d.diges();
}
}
匿名内部类的格式:
new 接口名(){
重写方法;
};
匿名内部类的应用:
匿名内部类一般 会作为方法的实参去传递 ,返回值去返回。
interface Digests{
void diges();
}
class ImplDigests {
public Digests show(){
return new Digests() {
@Override
public void diges() {
System.out.println("内部类。。。。。。。");
}
};
}
public static void main(String[] args) {
}
}
匿名内部类的使用场景:
当一个接口只有一个抽象方法的时候,用匿名内部比较方便,当一个接口中有多个抽象方法的时候,还不如去额外创建一个实现类
interface Digests{
void diges();
}
class ImplDigests {
public static void main(String[] args) {
show(new Digests() {
@Override
public void diges() {
System.out.println("蜀道之难,难于上秦天");
}
});
}
public static void show(Digests d){
d.diges();
}
}
Lambda表达式
目的:
就是为了优化匿名内部类的写法
格式说明和前提条件:
格式:()-> {重写内容}
():代表的是接口中抽象方法的参数
-> 这是一个固定的格式,意思是()里的参数可以给后面的{}使用
{}这里就是你需要重写的那个方法体
前提条件
1,Lanbda表达式必须有上下文的推导,必须根据逻辑让Jvm虚拟机推导出Lanbda实现的是哪个接口
2,Lanbda表达式只针对接口。
3,Lanbda表达式实现的接口必须只能有一个抽象方法
通过案例来找到前提条件
案例1;
interface Digests{
void diges();
}
class ImplDigests {
public static void main(String[] args) {
show(()->{
System.out.println("Digests接口中的diges方法....");
});
}
public static void show(Digests d){
d.diges();
}
}
案例2:
interface Digests{
void diges();
void method();
}
class ImplDigests {
public static void main(String[] args) {
show(()->{//这里编译报错
System.out.println("Digests接口中的diges方法....");
});
}
public static void show(Digests d){
d.diges();
}
}
解析:根据()格式的定义 我们就能推断出 接口只能有一个抽象方法。因为Lamdba只能重写接口中的一个方法。
案例3
interface Digests{
// void diges();
void method(int a);
}
class ImplDigests {
public static void main(String[] args) {
show((int s)->{
System.out.println(s);
System.out.println("Digests接口中的diges方法....");
});
}
public static void show(Digests d){
d.method(12);
}
}
如果抽象方法中有参数,在Lambda表达式中也要有参数
案例4:
interface Digests{
void method(int a);
}
class ImplDigests {
public static void main(String[] args) {
show((int s)->{
System.out.println(s);
System.out.println("Digests接口中的diges方法....");
});
}
public static void show(Digests d){
d.method(12);
}
}
案例5:
interface Digest{
void diges();
}
interface Digests{
void method();
}
class ImplDigests {
public static void main(String[] args) {
()->{//编译报错
System.out.println();
System.out.println("Digests接口中的diges方法....");
};
}
}
解析:因为Lambda表达式的格式只能表现出重写方法,而没有表达出重写的哪个接口中的方法。案例中有两个接口。所以我们要通过上下文来进行推导。
案例6
interface Digest{
void diges();
}
interface Digests{
void method();
}
class ImplDigests {
public static void main(String[] args) {
Digest digest=()->{//通过前年的引用来推导
System.out.println();
System.out.println("Digests接口中的diges方法....");
};
}
}
案例7
}
interface Digests{
void method();
}
class ImplDigests {
public static void main(String[] args) {
show(()->{//通过show方法的参数推导
System.out.println("秋天不回来");
});
}
public static void show(Digest digest){
digest.diges();
}
}
案例8
interface Digests{
void method();
void method1();
}
class ImplDigests {
public static void main(String[] args) {
Digests digests=()->{//编译报错
System.out.println("秋天不回来");
};
}
public static void show(Digest digest){
digest.diges();
}
}
解析:因为接口中存在两个抽象方法。
lambda优化:
- ()内如果只有一个参数 可以省略数据类型和小括号本身。
- ()内多个参数的 只能省略参数的数据类型不能省略小括号本身
- {}内如果只有一句话 可以省略分号 还有return 还有大括号本身
lambda和匿名内部类的区别:
- 匿名内部类 可以对 普通类 抽象类 接口 都可以使用但是lambda必须对接口使用
- 匿名内部类 可以要求 普通类 抽象类 接口 里面有多少抽象方法,lambda 要求接口中只能有一个抽象方法
- 匿名内部类会生成class文件,而lambda不生成class文件