内部类
什么是内部类
内部类是指在一个外部类的内部再定义一个类。内部类作为外部类的一个成员,并且依附于外部类而存在的。内部类可为静态,可用protected和private修饰(而外部类只能使用public和缺省的包访问权限)。
内部类的好处
内部类方法可以访问该类定义所在的作用域的数据,包括私有的数据
内部类可以对同一个包中的其他类隐藏起来,一般的非内部类,是不允许有 private 与protected权限的,但内部类可以
可以实现多重继承
当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷
每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。Java只能继承一个类,它的多重继承在我们没有学习内部类之前是用接口来实现的。但使用接口有时候有很多不方便的地方。比如我们实现一个接口就必须实现它里面的所有方法。而有了内部类就不一样了。它可以使我们的类继承多个具体类或抽象类。
内部类分类
- 成员内部类:
实例内部类、
静态内部类(类内部类) - 局部内部类
- 匿名内部类
1.实例内部类
语法:
Class Outer{
Class Inner{
}
}
实例内部类访问修饰符
实例内部类注意
- 实例内部类内部不允许定义任何静态内容
- 外部类静态方法中不能访问实例内部类数据
- 如果外部类成员变量、内部类成员变量、内部类方 法中局部变量同名
实例内部类的字节码文件名: 外部类名$内部类名.class
class Cat{
public Cat() {
super();
// TODO Auto-generated constructor stub
}
private double Weight;
public Cat(double Weight){
this.Weight = Weight;
}
//实例内部类
class CatBody{
private String color;
public CatBody(String color){
this.color = color;
}
public void show(){
//内部类访问外部类 :直接访问,因为内部类对象中都隐藏着一个外部类对象 外部类名.this
System.out.println("颜色:"+color+Cat.this.Weight);
}
}
public void dispaly(){
System.out.println("ggg"+this.Weight);
Cat cat = new Cat(22.2);
System.out.println(cat.Weight);
//外部类访问内部类的成员 用内部类的对象 来访问
CatBody body = new CatBody("白色");
body.show();
}
}
public class TestInner{
public static void main(String [] args){
Cat cat = new Cat(44.4);
cat.dispaly();
//创建内部类的对象
Cat.CatBody body = cat.new CatBody("黑色");
body.show();
// 匿名对象 只能使用一次
Cat.CatBody body1 = new Cat(55.5).new CatBody("蓝色");
body1.show();
}
}
2.静态内部类
语法
class Outer{
static Class Inner{
}
}
访问修饰符
静态内部类注意
- 内部类中不可以定义静态成员,但是可以定义Static final成员
- 外部类静态方法中不能直接访问实例内部类数据,但是可以访问Static final
- 外部类成元变量、内部类成员变量、内部局部变量重名
字节码文件名:外部类名$内部类名.class
class School{
//静态内部类,属于外部类所有
static class WaterFoundation{
int water = 65;
static int water1 = 100;
public void show(){
System.out.println(water);
}
public static void show1(){
System.out.println(water1);
}
}
public void manager(){
//访问水量
//访问静态内部类中的实例成员,用静态内部类对象访问
WaterFoundation w = new WaterFoundation();
w.show();
//访问静态内部类中的静态成员,用静态内部类名访问
WaterFoundation.show1();
}
}
public class TestStaticInner {
public static void main(String[] args) {
School school = new School();
school.manager();
//属于外部类,所以访问的时候不需要创建对象
School.WaterFoundation sw = new School.WaterFoundation();
sw.show();
sw.show1();//类名.静态成员
}
}
3.局部内部类
语法
方法中定义类,局部
class Outer{
public void function(){ class Inner{
}
}
}
局部内部类注意
- 局部内部类不能声明静态成员
- 局部内部类处于静态方法中只能访问外部类静态成 员
- 局部内部类处于实例方法中可访问外部类静态成员 和实例成员
- 局部类对局部变量的访问
字节码文件名 :外部类名$内部类名.class
public class TestLocalInner {
static int sn = 33;
int num = 44;
public void f(){
//8.0之前要加上final才可以访问
//8.0之后可以不加final,但是值不能更改,改了就不能访问
int x = 56;//局部变量
class Inner{
public void show(){
//实例方法 都可以访问
System.out.println(sn + num +x);
}
}
}
public static void main(String[] args) {
//局部内部类
class Base{
static final int n = 55;
int x = 55;
public void show(){
System.out.println(sn);
//静态方法内不能直接访问非静态成员,可以通过对象访问
//System.out.println(num);
}
}
class Sub extends Base{
int y = 66;
}
Sub sub = new Sub();
System.out.println(sub.x+","+sub.y);
}
}
4.匿名内部类(重点)
前提
必须继承一个父类或者实现一个接口
语法
new 父类(参数列表) 或者实现接口(){
//匿名内部类的类体部分
}
字节码文件名 :外部类名 $N.class
好处
注意:
- 必须实现一个类或一个接口。
- 不能定义静态数据
- 不能定义构造器
- 不能定义构造器
- 不能是抽象的
- 传参问题
- 同名问题
abstract class Base{
String s;
public void f(){}
public abstract void af();
}
class NoNameClass{
//父类类型 引用 = 匿名子类对象
//可以是静态的,不能是抽象的
Base base1 = new Base(){
public void f(){
System.out.println("f");
}
//子类独特的功能被屏蔽,不能用
public void func(){}
@Override
public void af() {
// TODO Auto-generated method stub
}
};
public void function(){
new Base(){
public void f(){
System.out.println("f");
}
//子类独特的功能被屏蔽,不能用
public void func(){}
@Override
public void af() {
}
}.func();
}
}
public class TestNoNameInner {
public static void main(String[] args) {
NoNameClass n = new NoNameClass();
n.base1.f();
//用的接口名称,但是创建的是子类的对象
new Base(){
public void f(){
System.out.println("f");
}
@Override
public void af() {
// TODO Auto-generated method stub
}
}.f();
}
}
Lambda
函数式接口
只含有一个抽象方法的接口称为函数式接口
Lambda语法
(参数)—>(语句;)
Lambda表达式:
作用:替换匿名内部类的语法。
注意:只能能应用于 函数式接口。
方法引用
Lambda中只有一行代码,代码中只调用了一个方法
//函数式接口
@FunctionalInterface
interface la{
//只含有一个抽象方法的接口称为函数式接口
// int func();
// void func();
// void func(int n );
int func (int n);
/* default void df(){}
static void sf(){}*/
}
//方法一
/*class LaImpl implements la{
@Override
public void func() {
// TODO Auto-generated method stub
}
}*/
class LaDemo{
//方法二 匿名内部类的方式
/* la a = new la(){
public void func(){
System.out.println("func");
}
};*/
//方法三 Lambda无参数无返回值
// la a1 = () -> {System.out.println("func");};
// la a2 = () -> System.out.println("func");
//无参数有返回值
/*la a1 = () -> {return 11;};
la a2 = () -> 11;*/
//形参列表
// la a1 = (m) -> {System.out.println(m);};
//带参数带返回值
//la a1 = (n)-> {return n;};
//la a1 =n-> n;
}
public class TestLambda1 {
public static void main(String[] args) {
LaDemo demo = new LaDemo ();
// demo.a.func();
}
}
匿名内部类和 Lambda的区别
- Lambda只针对于函数式接口,匿名内部类 对接口,普通类,抽象类都可以
Lamdba表达式调用Arrays中的方法
//Lambda表达式调用Arrays中的方法
import java.util.Arrays;
import java.util.function.IntUnaryOperator;
/**
*
* @author daidai
*
*/
public class Ex2 {
public static void main(String[] args) {
int [] arr ={1,2,3,4,5};
System.out.println(Arrays.toString(arr));
//用一元运算的结果对数组元素替换
//数组下标的替换
Arrays.parallelSetAll(arr, new IntUnaryOperator(){
public int applyAsInt(int operand){
return operand;//下标
}
});
Arrays.setAll(arr, o->{return o; });
//用二元运算结果
Arrays.parallelPrefix(arr, new IntBinaryOperator(){
@Override
public int applyAsInt(int left ,int right){
//right是当前元素,left是前一个元素
//当前元素是第一个时,前一个元素是1
return left * right;
}
});
String s = Arrays.toString(arr);
System.out.println(s);
//遍历数组
// Stream流的用法
Arrays.stream(arr).forEach(e -> {System.out.println(e);});
//方法引用
Arrays.stream(arr).forEach(System.out :: println);
}
}