内部类:声明在一个类的【内部】,包含内部类的类,被称为外围类。如果一个类只有外围类,没有内部类——顶级类。
根据内部类声明的位置
(1) 静态成员类(了解)
(2) 实例成员类(了解)
(3) 局部类(本地类)(掌握)
(4) 匿名类(掌握)
class Outer{
// 就将静态成员类看成“静态成员”
public static class Inner{
// 在静态成员类中,可以访问到静态成员,但是不能访问到外围类的实例成员。
public void f(){
System.out.println(x);
x=2;
}
}
public static int x;
// Inner i=new Inner(); 可以
public staitc void m1(){
Inner i =new Inner();
}
public class test{
public static void main(String[] args){
System.out.println(Outer.x);
Outer.Inner oi=new Outer.Inner();
}
}
}
实例成员类,在外围类的内部可以直接访问,在外围类的外部,必须通过外围类的对象才能访问
Outer.Inner i=o.new Inner();
在实例成员类中,访问外部成员可以任意访问静态与非静态(原因:实例成员是非静态的,在非静态的环境下可以访问静态、非静态)
当实例成员类的对象产生的时候,外围类的对象一定产生了。
【特殊的情况】static + final 也可以访问到
实例成员类中,不能够声明任何的静态成员、静态初始化块
为什么?假如可以 Outer.Inner.k ,Inner本身就需要Outer对象产生。
【特殊】使用static final定义的常量可以定义在实例内部类中。(为什么?)跟所有对象都没关系,甚至于跟类都没有关系。在编译器,甚至于在字节码文件中,根本就不存在final类型变量名。敞亮的存储只要存在静态区,访问的时候,直接访问存储的地址
public static final int j=123;
实例成员内部类会遮蔽外围类的同名成员变量。
相当于:成员变量
局部变量会遮蔽成员变量,内部类的成员变量也会遮蔽外围的成员变量
class Outer{
public int x=123;
public static int y;
public static final int z=123;
public class Inner{
// 三个静态成员都不能在实例成员类中声明
// public static int k;
// public static void test1(){}
// static {}
// public static final int j=123;常量可以声明
public int x=456;
public void test(int x){
x=789;
System.out.println(x);
// 成员实例类中访问,成员实例类中的成员变量。(局部变量遮蔽成员变量)
System.out.println(this.x);
// System.out.println(y);
// 非要实例成员类中访问外围类的成员变量:可以用外围类的名.内部类的实例.变量。(了解)
System.out.println(Outer.this.x);
f1();
f2();
}
}
public void f1(){
Inner i=new Inner();
}
public static void f2(){
}
}
声明在类内部,同时声明在局部范围内(方法、语句块)里面声明的类,被称为是本地类(局部类)
理解成:局部变量
【访问权限修饰符(没有)】【修饰符static(没有)】 class 内部类的名字{类体}
同局部变量,从声明的位置到最小的语句块
class Outer{
public static int x=1;
public int y=2;
public void fun(){
class Inner{//本地类,局部类
public int x=1;
public void f1(){
System.out.println(x);
System.out.println(y);
}
}
}
}
4.常见应用:在外围类的外面,声明一个接口,在外围类的方法中,声明一个局部类,这个局部类实现外部的接口,在实现中返回局部类的对象(跟接口对应)
一句话:在外围类的方法中,实现接口中的方法
这样的局部类对象就可以在作用域外有效
【好处】:
(1)不需要当前的类本身取实现某个接口,只需要类中的某个方法实现接口即可。
让接口和实现类之间“解耦”—解除耦合—能够让类和类之间的设计变得更加灵活
类的拆装非常方便。
(2)扩大局部类的作用域(局部类是一个实现类)
class Outer{
public void a(){
class Phone implements Mobile{//Phone是内部类
public void call(){
System.out.println("使用Phone打电话")
}
}
// 想去调用call方法,必须先创建Phone
Phone p=new Phone;
p.call();
}
// 思考,在Outer中,如果想调用到Phone对象?
public Mobile getMobile(){
class Phone implements Mobile{//Phone是内部类
public void call(){
System.out.println("使用Phone打电话")
}
}
// 想去调用call方法,必须先创建Phone
Mobile p=new Phone;
// p.call();
return p;
}
// 思考,在Outer中,如果想调用到Phone对象?
public void other(){
Mobile phone=this.getMobile();
}
}
//class Phone2{
//有的时候,值是希望Phone2下的某一个方法,去实现Mobile
//相当于不想让Phone2类本身实现的接口越来越多
//只希望通过某个方法来实现Mobile,如果不希望Phone2实现Mobile,只需要注释掉该方法就可以
}
interface Mobile{
void call();
}
public class test{
public static void test(Mobile m){
m.call();
}
public static void main(String [] args){
Outer o=new Outer();
// o.a();
Mobile phone =o.getMobile();
phone.call();
Outer2 o2=new Outer2();
Mobile ipad=o2.getMobile();
test(phone); //利用到多态参数
test(ipad); //利用到多态参数
}
}
【练习】让ipad打电话,创建新的Outer2
class Outer2{
public Mobile getMobile(){
class Ipad implements Mobile{
@Override
public void call(){
System.out.println("ipad进行打电话");
}
}
return new Ipad();
}
}
没有名字的局部类
【意义】如果类产生的对象,只使用一次,代码非常简洁。
【语法】return new 接口(){实现接口的实现类}
【规则】同局部类
【定义】方法的{}内部就相当于实现类,new 后面的相当于要实现的接口
【容易误解的地方】new 后面是接口类型,new 接口(){实现接口的实现类(匿名)};
【使用的注意】局部类(没有名字)实现外面的接口;一定是接口实现本身非常简单;匿名类只使用一次比较方便
public class Outer{
public 接口类型 fun(){
class Inner(可以去实现外部接口){
}
Inner inner=new Inner();
return Inner()
}
}
简化:
public class Outer{
public 接口类型 fun(){
Inner inner=new Inner(){
内部类的类体
}
return inner;
}
}
更简化:
public class Outer{
public 接口类型 fun(){
return new Inner(){
内部类的类体
}
}
}
class Outer2{
public Mobile getMobile(){
Mobile pad=new Mobile(){//{}内部就相当于实现类,new后面的类型,相当于要实现的接口
@Override
public void call(){
System.out.println("ipad进行打电话")
}
};
return pad;
}
}
再简化:
return new Mobile(){//{}内部就相当于实现类,new后面的类型,相当于要实现的接口
@Override
public void call(){
System.out.println("ipad进行打电话")
}
};
【练习】用phone实现匿名类
return new Mobile(){
@Override
public void phone(){
System.out.println("用phone进行打电话");
}
};
【练习】
javaTeacher 和 PythonTeacher
(1)javaTeacher和PythonTeacher形成实现类(局部类),方法javafun和pythonfun
(2)javaTeacher和PythonTeacher形成匿名类
interface Teacher{
void teach();
}
class Outer {
public Teacher javafun(){
// class JavaTeacher implements Teacher{
// public void teach(){
// System.out.println("java讲师讲课");
// }
// }
return new Teacher(){
public void teach(){
System.out.println("java讲师讲课");
}
};
}
public Teacher pyhtofun(){
// class PythonTeacher implements Teacher{
// public void teach(){
// System.out.println("python讲师讲课");
// }
// }
return new Teacher(){
public void teach(){
System.out.println("python讲师讲课");
}
};
}
}
public class Day9_1_InnerClass{
public static void test(Teacher t ){
t.teach();
}
public static void main(String[] args) {
Outer o=new Outer();
Teacher javat=o.javafun();
Teacher pythont=o.pyhtofun();
javat.teach();
pythont.teach();
test(javat);
test(pythont);
}
}
1.8之后引入的重要概念
【目的】让匿名类的使用上更加简化
【函数式接口】
函数式接口、函数式编程。。。
【定义】当一个接口中,必须【只声明一个】【未实现的抽象方法】,称为函数式接口。
函数式接口(可以有抽象方法也可以有非抽象)
【要求严格的原因】在使用lambda表达式实现接口的时候,默认实现的就那一个未实现的接口。
【java中提供了标记】@FunctionalInterface, 这种就是函数式接口,为了容易识别函数式接口,不加也没错
@FunctionalInterface
interface F{
void m();
default void k(){
}
String toString();//F接口默认继承了Object,Object下是有toString方法已实现
}
【lambda作用】专门用来实现函数式接口的。
最终都是一个表达式,相当于实现类—匿名实现类----表达式(使用表达式实现的时候,没有方法的名字)
【原理】外围类不需要,外围类下的方法也不需要,实现类都不需要,简化实现类
【语法】可以看成是一个匿名的方法
[return] (参数列表) -> {方法的具体实现}
参数列表:在实现接口中方法的时候,传入的参数
{}:是具体实现
return可以省略
关于mobile ipad实现案例
实现类可以使用lambda表达来表示
【比匿名类还要省略掉的内容】
【本质】通过lambda表达式实现
【Lambda表达式的结果】使用接口类型引用实现类对象(父类引用指向子类对象)
class Outer{
public Mobile getMobile(){
return ()->{
System.out.print("ipad进行打电话")
};
}
}
class Outer2{
public Mobile getMobile(){
return new Mobile(){//{}内部就相当于实现类 new 相当于要实现的接口
@Override
public void call() {
System.out.println("ipad进行打电话");
}
};
return ()->{
System.out.println("ipad进行打电话");
};
}
}
interface Mobile{
void call();
}
public class Day9_1_InnerClass{
public static void test(Mobile m){
m.call();
}
public static void main(String[] args) {
// Outer2 o2=new Outer2();
// Mobile ipad=o2.getMobile();
Mobile ipad=()->{
System.out.println("ipad进行打电话");
};//如果需要在一个类中,实现Mobile中的唯一抽象方法,只需要在测试调用中,加lambda表达式即可
// ()传参数 {}传实现
test(ipad);
}
}
当lambda表达式中有返回值的时候。
@FunctionalInterface
interface X{
int value(int a);
}
class Outer{
public X getX(){
class Inner implements X{
@Override
public int value(int a) {
return a*10;
syso.out.print(a*10)
}
}
X x=new Inner();
return x;
匿名类
return new X(){
public int value(int a) {
return a*10;
}
};
lambda表达式
return (int b)->{
return b*10;
};
}
}
public class Day9_1_InnerClass{
public static void main(String[] args) {
X x=(int b)->{return b*10;};
System.out.println(x.value(50));
}
}
【lambda表达的简化规则】
(1)参数列表,类型可以省略,如果只有一个参数()也可以省略,如果没有参数()就不能省略
(2)return无论是否有返回值,都可以省略
(3)如果方法体只有一句话,{}可以省略
public class Day9_1_InnerClass{
public static void main(String[] args) {
X x= b->b*10;//实现X接口中的value方法。
System.out.println(x.value(50));
}
}
【应用】
编写一个方法,针对一个人员的数组实现的方法,处理人员的数组
Person: name age height weight…
{Person1,Person2,Person3…}
依次判断数组中的每个元素,是否符合指定的条件,
如果符合指定的条件,就将Person元素输出.
class Person{
private String name;
private int age;
private int height;
private int weight;
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 getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public Person(String name, int age, int height, int weight) {
super();
this.name = name;
this.age = age;
this.height = height;
this.weight = weight;
}
}
//实现单一规则可以 测试类
interface Rule{
boolean check(Person p);
}
class MyRule1 implements Rule{
@Override
public boolean check(Person p) {
return p.getAge()>=18&&p.getAge()<=52;
}
}
class Day9_1_InnerClass{
// public void dealwith(Person [] persons,int min,int max){
public void dealwith(Person [] persons,Rule r){
// 比如,筛选>=18的人
for (Person p:persons){
// if(p.getAge()>=min&&p.getAge()<=max){
if(r.check(p)){
System.out.println(p.getName());
}
}
}
// 直接使用java已提供的API Predicate接口,是函数式接口 test方法
public void dealwith1(Person [] persons,Predicate<Person> r){
// 比如,筛选>=18的人
for (Person p:persons){
// if(p.getAge()>=min&&p.getAge()<=max){
if(r.test(p)){
System.out.println(p.getName());
}
}
}
public static void main(String[] args) {
Day9_1_InnerClass c=new Day9_1_InnerClass();
Person[] persons=new Person[3];
persons[0]=new Person("张三",18,150,130);
persons[1]=new Person("李四",20,180,130);
persons[2]=new Person("王五",52,150,130);
c.dealwith(persons, new MyRule1());//有实现类,有了规则,按照实现类删选Person的规则
c.dealwith(persons, new Rule(){//匿名实现类
public boolean check(Person p2){
return p2.getAge()>=18&&p2.getAge()<=52;
}
});
// c.dealwith(persons,(Person p2)->{return p2.getAge()>=18&&p2.getAge()<=52;});//lambda表达
c.dealwith(persons,p2->p2.getAge()>=18&&p2.getAge()<=52);//lambda表达
c.dealwith(persons,p2->p2.getHeight()>170);//lambda表达
c.dealwith1(persons,p2->p2.getHeight()>170);//lambda表达
}
}
【本质前提】:必须先要有接口,然后在工具类中或者自身类中进行实现。
简化调用——方法引用
如果已有方法已经实现了相关的内容,我们可以直接使用方法的引用,来调用实现的内容。
类型 | 示例 |
---|---|
(1)引用静态方法(重要) | ContainingClass::staticMethodName |
(2)通过对象引用实力方法 | ContainingObject::instanceMethodName |
(3)通过类引用实例方法 | ContainingType::methodName |
(4)引用构造器(了解) | ClassName::new |
通过已有的静态方法来实现实现类
class Person{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(String name) {
super();
this.name = name;
}
}
interface Friend{
void makeFriend(Person p1,Person p2);
}
class ToolStatic{
// makeFriend的名字参数,都跟接口中一致
public static void makeFriend(Person p1,Person p2){
System.out.println("交朋友的方法");
}
}
public class Day10_1_InnerClass {
public static void test(Friend f){
Person p1=new Person("张三");
Person p2=new Person("李四");
f.makeFriend(p1, p2);
}
public static void main(String[] args) {
// 希望能够实现Friend接口
// 如果使用lambda表达式来实现,【获得实现接口的实现类对象】。
// Friend f1=(Person p1,Person p2)->{
//// 希望在这里实现真正交朋友的方法
// ToolStatic.makeFriend(p1, p2);
// };
// 简化
// Friend f1=(p1, p2)-> ToolStatic.makeFriend(p1, p2);
// 更加简化 :静态方法:类名::静态方法名
// 【省略的内容】:传过来的参数(形式参数),方法名字中的参数(实际参数)
Friend f2=ToolStatic::makeFriend;
【上面功能】:将Friend接口中的两个形式参数,传递给ToolStatic类下的makeFriend方法
test(f2);
}
}
【问题】参数
(1)如果达到方法引用的效果,必须是函数式接口。
(2)参数,直接到接口下找,对应。
要求()中的形式参数 ,必须实际参数(类中的静态方法中(形式参数))一一对应。
静态方法 跟重写接口中方法一样。
interface Friend{
void makeFriend(Person p1,Person p2);
}
class ToolStatic{
// makeFriend的名字参数,都跟接口中一致
public void makeFriend(Person p1,Person p2){
System.out.println("交朋友的方法");
}
}
public class Day10_1_InnerClass {
public static void test(Friend f){
Person p1=new Person("张三");
Person p2=new Person("李四");
f.makeFriend(p1, p2);
}
public static void main(String[] args) {
ToolStatic ts=new ToolStatic();
Friend f2=ts::makeFriend;
test(f2);
}
}
三、通过类型:方法的引用
让Person类自己去实现Friend接口中的东西
class Person{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(String name) {
super();
this.name = name;
}
// 实现了接口中的方法
public void makeFriend(Person p2){
System.out.println("交朋友的方法");
}
}
interface Friend{
void makeFriend(Person p1,Person p2);
}
public class note1 {
public static void test(Friend f){
Person p1=new Person("张三");
Person p2=new Person("李四");
f.makeFriend(p1, p2);
}
public static void main(String[] args) {
// Friend f2=(p1,p2)->{p1.makeFriend(p2);};
// Friend f2=(p1,p2)->p1.makeFriend(p2);
Friend f2=Person::makeFriend;
test(f2);
}
}
**[为什么]*可以通过类名,调用类下方法。
因为接口下void makeFriend(Person p1,Person p2); 有两个参数
“实现类” Person中有 public void makeFriend(Person p2){}
会自动将第一个参数当成Person对象,第二个参数当成实现方法中的参数。