**
内部类
**
内部类就是定义在一个类里面的类。有三种:
1.成员内部类
2.局部内部类
3.匿名内部类
我们先来看第一种成员内部类:
定义一个外部类(Body)和一个成员内部类(Heart),格式如下:
public class Body {
public class Heart
{
String name="heart";
public void methodHeart()
{
System.out.println("heart类方法");
System.out.println("我是"+name);
System.out.println("我叫"+Body.this.name);//与使用super不同,需要这样来访问外部类成员变量
}
}
private String name="gfx";
public void methodBody()
{
System.out.println("body类方法");
new Heart().methodHeart();
}
}
有五点注意事项:
1.作为外部类权限修饰符只有public和(default)
2.成员内部类,权限修饰符四种都可以
3.内部类可以随意访问外部类的成员变量和成员方法
4.内部类想使用外部类的成员变量要使用——外部类类名.this.成员变量名的格式。
5.外部类想要使用内部类的成员方法要使用对象调用(间接使用),外部类methodBody方法调用内部类methodHeart方法。
我们看一下main方法的运行结果
public static void main(String[] args) {
Body body=new Body();
Body.Heart heart=new Body().new Heart();//创建一个内部类对象
body.methodBody();
System.out.println("==================");
heart.methodHeart();
}
结果
Body类方法
heart类方法
我是heart
我叫gfx
==================
heart类方法
我是heart
我叫gfx
第二种局部内部类:
局部内部类是定义在一个方法之内的类,不可以被外面的方法访问到,并且定义的时候不可以写权限修饰符。下面在Body类中定义一个methodBody2方法,在其中定义一个局部内部类(Brain):
public void methodBody2()
{
int num=10;
class Brain//局部内部类,不能写权限修饰符
{
public void methodBrain()
{
System.out.println("Brain(局部内部类)类方法");
}
public void getNum()
{
System.out.println(num);
}
}
Brain brain=new Brain();//因为在方法外面就不可以使用Brain类了,所以要在方法内部进行定义
brain.methodBrain();
}
一个局部内部类的使用,一个非常重要的注意事项是,局部内部类访问所在方法的局部变量的时候,该变量一定要是有效final的。
所谓的有效final的局部变量就是该局部变量不可以被改变,methodBody2方法中的局部变量num,并没有加final关键字来修饰,但是num没有被改变,所以num也是有效final的局部变量。
要这样做的原因是局部变量在方法结束就从内存消失了,但局部内部类不一定会消失,所以在使用局部变量的时候就复制该值,所以要求保持不变。
在main中运行
public static void main(String[] args) {
body.methodBody2();
}
结果
Brain(局部内部类)类方法
第三种匿名内部类,使用匿名内部类时,一般都是接口的实现类或者父类的子类只需要用唯一的一次,那么使用匿名内部类可以大大节省时间。
我们来定义一个简单的接口
public interface MyInterface {
public void method();
}
现在我们想使用这个接口,之前都是先创建一个实现类,然后重写抽象方法,然后在main中创建对象等等步骤,非常麻烦,现在我们使用匿名内部类可以大大减少代码量。
在mian方法中使用
public static void main(String[] args) {
MyInterface obj=new MyInterface() {
@Override
public void method()
{
System.out.println("使用了匿名内部类");
}
};
obj.method();
}
我们可以看到代码量大大的减少,我们直接在main中重写了接口中的抽象方法,然后通过接口的一个对象使用了它完成了任务。
结果
使用了匿名内部类
但是还可以进一步的简化,若对象也只用一次,比如上面的obj,那么可以通过匿名对象来使用匿名内部类。
public static void main(String[] args) {
new MyInterface() {
@Override
public void method()
{
System.out.println("匿名对象使用了匿名内部类");
}
}.method();
}
结果
匿名对象使用了匿名内部类
匿名内部类虽然好用但是只限于某些使用一次的方法,若某个方法要经常被使用,那么还是常规模式比较简单。
**
Lambda表达式
**
在编写代码的时候,总是有各种各样的重复的冗余的代码,真正有意义的代码往往很短,Lambda表达式删除了冗余。Lambda表达式起到的作用就像是加强版的匿名内部类。
Lambda表达式可以简单什么程度呢,我们先来看一下Lambda表达式的标准格式:
1.一些参数
2.一个箭头
3.一段代码
(参数列表)->{一些重写方法的代码};
()就是接口中抽象方法的参数列表
->就是传递的意思,把参数传递给方法体
{}是重写接口的抽象方法的方法体
可能你看到这个一头雾水,我们来举个例子,先定义一个类叫Player
public interface Player {
public void play();
}
再定义一个局部方法来调用play函数
public static void invokePlayer(Player player) {
player.play();
}
重点来了,我们在main函数中使用匿名内部类和Lambda表达式对比一下
public static void main(String[] args) {
new Player()
{
@Override
public void play() {
System.out.println("打游戏");
}
}.play();//匿名内部类
}
invokePlayer(()->{
System.out.println("打游戏");
});//Lambda表达式
结果
打游戏
打游戏
我来解释一下,Lambda表达式就是删除代码的冗余。在使用匿名内部类时重写play方法是一件很繁琐的事情,那么我们只留下关键的代码部分,如我上面所说(再重复一遍):
**
1.()就是接口中抽象方法的参数列表(这里就是play方法,play方法没有参数,所以()留空)
2.->就是传递的意思,把参数传递给方法体
3.{}是重写接口的抽象方法的方法体(方法体就是 System.out.println(“打游戏”);)
**
上面的play方法是无参无返回值的方法,我们再来看一个有参有返回值的方法
定义一个简单Person类
public class Person {
private String name;
private int age;
public Person(String name,int age)
{
this.name=name;
this.age=age;
}
public int getAge()
{
return age;
}
@Override
public String toString()
{
return "姓名 "+name+" 年龄 "+age;
}//方便输出
}
在main中使用Lambda表达式来排序(利用了Arrays类的sort方法)
public static void main(String[] args) {
Person[] array= {
new Person("gfc",20),
new Person("zbh",21),
new Person("paco",22)
};//创建一个Person数组
Arrays.sort(array,(Person o1,Person o2)->{
return o2.getAge()-o1.getAge();
});//这个其实重写了sort的Compare方法,参数是Person o1,Person o2,返回值是o2.getAge()-o1.getAge(),按照年龄降序排列
for(int i=0;i<array.length;i++)
{
System.out.println(array[i]);
}
}
结果
姓名 paco 年龄 22
姓名 zbh 年龄 21
姓名 gfc 年龄 20
我们看到带参带返回值与无参无返回值并没有太大的区别,但是有一点,Lambda表达式还可以更加简单,规则:
1.(参数列表):扩号中的参数类型可以不写
2.(参数列表):括号中的参数若只有一个,那么()与数据类型都可以不写
3.{方法体}:若代码只有一行,无论是否有返回值,那么{},return,分号都可以省略(三个要一起省略)
我们来按照这个规则看一下上面两个例子的简化版本
public static void main(String[] args) {
invokePlayer(()->System.out.println("打游戏"));//只有一行代码
Arrays.sort(array,(o1,o2)->o2.getAge()-o1.getAge());//只有一行代码且有参数
for(int i=0;i<array.length;i++)
{
System.out.println(array[i]);
}
}
结果
打游戏
姓名 paco 年龄 22
姓名 zbh 年龄 21
姓名 gfc 年龄 20
以上就是这次日志的全部内容,大家再见啦!