内部类
/*
* 内部类:
* 1.成员内部类,类没有使用static修饰
* 2.静态内部类,类使用了static修饰
* 3.局部内部类,定义在方法中
* 4.匿名内部类,没有具体的类名,并且仅使用一次,属于局部内部类
* ps:对于每个内部类通过java编译之后都会产生一个独立的字节码文件
* 成员内部类:外部类的类名$成员内部类的类名.class
* 静态内部类:外部类的类名$静态内部类的类名.class
* 局部内部类:外部类的类名$数字内部类的类名.class
* 匿名内部类:外部类的类名$数字.class
*/
内部类也是类,可extends可implements
- 成员内部类:
/*
* 成员内部类:
* 声明在类的内部不使用static修饰的类
* 成员内部类的权限修饰符默认是default /public/private/protected
* 成员内部类属于外部的对象,不属于外部类本身
*/
public class InnerClass {//外部类,可以使用final/abstract修饰
//不能用static修饰
String name = "小白";
public void display(){
System.out.println("外部类的成员方法");
//外部类中调用内部类的属性 --> 创建内部类对象
/*
* 1.先创建外部类对象
* 2.通过外部类对象创建内部类对象
*/
// InnerClass innerClass = new InnerClass();
// InnerClass.Inner inner = innerClass.new Inner();
// inner.show();
//简化成一步
InnerClass.Inner inner2 = new InnerClass().new Inner();
inner2.show();
}
//成员内部类是写在类的内部,并且与成员变量或成员方法平级
public class Inner{
//定义成员属性和行为,但是不能定义static修饰的
String name = "小黑";
//在内部类中访问同名属性问题
public void show(){
String name = "小红";
//就近原则,打印方法体内部的变量 (小红)
System.out.println(name);
//内部类的成员变量 (小黑)
System.out.println(this.name);
//外部类的name (小白)
System.out.println(InnerClass.this.name);
//若不是同名,并且是成员
// display();
}
}
public static void main(String[] args) {
new InnerClass().display();
// InnerClass.Inner inner2 = new InnerClass().new Inner();
// inner2.show();
}
}
非static的内部类,在外部类加载的时候,并不会加载它,所以它里面不能有静态变量或者静态方法。
1、static类型的属性和方法,在类加载的时候就会存在于内存中。
2、要使用某个类的static属性或者方法,那么这个类必须要加载到jvm中。
基于以上两点,可以看出,如果一个非static的内部类如果具有static的属性或者方法,那么就会出现一种情况:内部类未加载,但是却试图在内存中创建static的属性和方法,这当然是错误的,即类还不存在,但却希望操作它的属性和方法。
- 静态内部类:
package note;
/*
* 静态内部类:
* 定义在类的内部,并且使用static修饰的类
* 静态内部类的权限修饰符默认是default /public/private/protected
*/
package day01;
public class Outter {//外部类绝对不能使用static修饰
String name = "小白";
static int age = 20;
public void display(){
System.out.println("外部类的成员方法");
//外部类成员方法访问静态内部类的静态变量
System.out.println(Inner.name);
}
public static void showInfos(){
System.out.println("外部类的静态方法");
//外部类的静态方法中访问静态内部类的静态变量
System.out.println(Inner.name);
/*
* 创建静态内部类对象
*/
Outter.Inner inner = new Outter.Inner();
inner.show();
}
//静态内部类
public static class Inner{
//可以定义静态属性和方法,也可以定义成员变量和方法
static String name = "小黑";
public void show(){
System.out.println("----------------------");
System.out.println("静态内部类访问外部类变量");
//访问外部类的静态属性,不重名直接访问,重名使用类名确认属于谁
System.out.println(Outter.age);
//访问外部类的成员变量,
//因为静态内部类不会持有外部类的对象
//所以不能通过Outter.this访问,只能通过创建对象
System.out.println(new Outter().name);
System.out.println("----------------------");
System.out.println("静态内部类访问外部类方法");
//外部类的静态方法
// showInfos();
//成员方法访问方式
new Outter().display();
}
}
public static void main(String[] args) {
Outter outter = new Outter();
outter.display();
System.out.println("----------------------");
outter.showInfos();
}
}
- 局部内部类:
package note;
/*
* 局部内部类:(一个基本上不会使用的类)
* 定义在方法体内的内部
* 不能使用public/protected/static修饰,不能包含静态成员
*
*/
package day01;
public class Outter {
String name = "小白";
public void show(){
//在方法中创建局部变量 -->JDK1.8之前必须显式的声明该"变量"为常量才可以
//1.8开始 final可以不写,只要局部内部类访问该变量默认添加
final int age = 30;
//局部内部类
class Inner{
//不可以定义静态属性和方法
String info = "信息";
public void display(){
//访问外部类的成员属性
System.out.println(name);
//在局部类中访问方法中的局部变量,这个局部变量必须使用final修饰
//不能称为变量 而是 常量
/*
* 正常方法的调用通过压栈和弹栈完成
* 因为局部内部类是声明在方法的内部,所以此处存在一个引用问题
* 一旦方法弹栈,整个方法都销毁了
* 局部内部类的空间是在堆中,虽然栈中的方法弹栈销毁了,但是堆这段空间不能及时回收(GC)
* 若局部内部类中引用了方法中的变量,此时就会出现引用问题
* 为了防止这个问题,将数据存储到常量池中,这样就可以避免问题发生了
*/
System.out.println(age);
System.out.println(info);
}
}
//局部内部类只能在方法中创建对象
new Inner().display();
}
public static void main(String[] args) {
new Outter().show();
}
}
- 匿名内部类:
实现抽象类,接口,甚至重写普通类
接口多态:
public interface ISmoking {
/**
* 抽某个品牌的香烟
* @param name 香烟名
*/
void smoke(String name);
}
public class Man implements ISmoking {
@Override
public void smoke(String name) {
System.out.println(name+"牌子的烟");
}
}
public class Outter {
public static void main(String[] args) {
Man man = new Man();
man.smoke("小熊猫");
//接口是存在多态的 --> 接口是一个特殊的父类
ISmoking iSmoking = new Man();
iSmoking.smoke("芙蓉王");
}
}
运行结果:
小熊猫牌子的烟
芙蓉王牌子的烟
/*
* 匿名内部类:(没有名字)
* 可以写在类中,也可以写在方法体中,是一个特殊的局部内部类
* 本身是没有构造方法的,需要使用父类的构造方法来完成初始化
* 匿名内部类中是不会定义属性和方法的,没有意义不能调用
* 匿名内部类是用来处理接口实现的
* ps:若只需要使用一次接口中的方法,推荐使用匿名内部类,节省代码
*/
定义在方法中:
public static void main(String[] args) {
//匿名内部类
ISmoking isk = new ISmoking() {
@Override
public void smoke(String name) {
System.out.println(name+"的烟");
}
};
isk.smoke("中华");
}
定义在类中:
public class Outter {
public static void main(String[] args) {
new Outter().show();
}
//为了方便在类中使用,会将匿名内部类作为类的属性存在
ISmoking instance = new ISmoking() {
/*
* 在匿名内部类中定义了属性和方法
* 这属性和方法无法被外部调用所以会成为垃圾代码
* 此种定义无意义
*/
// int money = 50;
// public void doSomething(){
// System.out.println("演示");
// }
/*
* 开发中需要在匿名内部类中所实现的的方法中添加其他变量,
* 最好的方式是定义在方法的内部,而不是成员变量
* 若定义成员变量,必须是约定好的
*/
@Override
public void smoke(String name) {
System.out.println(name+"牌子的烟");
}
};
public void show(){
instance.smoke("利群");
}
}
重写普通类:
public class InnerClass {
public void display() {
System.out.println("InnerClass.display");
}
public void display2() {
InnerClass.Inner inner2 = new InnerClass().new Inner();
inner2.show();
}
public class Inner {
public void show() {
System.out.println("InnerClass.Inner.show");
}
}
}
class Outter {
public static void main(String[] args) {
//匿名内部类实现普通类
InnerClass innerClass = new InnerClass() {
@Override
public void display() {
super.display();
System.out.println("匿名内部类实现普通类 重写display方法");
}
@Override
public void display2() {
super.display2();
System.out.println("匿名内部类实现普通类 重写display2方法");
}
};
System.out.println("===============匿名内部类 --> 普通类===============");
innerClass.display();
System.out.println("========匿名内部类 --> 普通类 --> 成员内部类========");
innerClass.display2();
}
}
Lambda表达式:
Lambda表达式是Java8中的新特性
Java8中引入Lambda表达式,使得java可以函数式编程,在并发性能上迈出了实质性的一步。
什么是函数式编程?
函数式编程(英语:functional programming)或称函数程序设计,又称泛函编程,是一种编程范型,它将电脑运算视为数学上的函数计算,并且避免使用程序状态以及易变对象。函数编程语言最重要的基础是λ演算(lambda calculus)。而且λ演算的函数可以接受函数当作输入(引数)和输出(传出值)。ps:λ这个符号可以在搜狗输入法的符号中显示
而在面向对象编程中,面向对象程序设计(英语:Object-oriented programming,缩写:OOP)是种具有对象概念的程序编程范型,同时也是一种程序开发的方法。它可能包含数据、属性、代码与方法。对象则指的是类的实例。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性,对象里的程序可以访问及经常修改对象相关联的数据。在面向对象程序编程里,计算机程序会被设计成彼此相关的对象。
Lambda表达式可以替代匿名内部类完成接口的实现:
public class TestA {
public static void main(String[] args) {
//实现接口匿名内部类
InterfaceA interfaceA = new InterfaceA() {
@Override
public void show() {
System.out.println("匿名内部类实现的借口");
}
};
interfaceA.show();
//实现接口的Lambda表达式
InterfaceA interfaceA2 = () -> {
System.out.println("Lambda表达式实现的接口");
};
interfaceA2.show();
}
}
//函数式接口 --> 明确表示当前接口是可以使用lambda表达式实现的
//函数中必须提供一个要被实现的方法
@FunctionalInterface
interface InterfaceA{
void show();
}
运行结果:
匿名内部类实现的借口
Lambda表达式实现的接口
语法:(参数) -> { 具体实现的方法逻辑 };
1.(参数)
参数列表中的数据类型是可以省略,直接写形参名,没有参数会空()的形式,只有一个参数,小括号都可以省略
例如:(a,b) --> 两个参数 () --> 没有参数 (a) ---> 一个参数 / 直接写a,省略小括号
lambda表达式中的参数对应是接口中方法的参数列表,因为接口中的参数列表中数据类型已经明确表示,所以在lambda中是可以省略的,若需要添加参数类型,一定要和接口中方法的参数类型一致
2.-> 箭头 (语法要求必须写,必须是英文字符 减号 + 大于号)
3.代码块{ }
代码块是方法的具体实现,方法逻辑都需要写到这个代码块中
ps:Lambda允许省略{ } --> 只有一句可以省略,Lambda允许省略return --> 只有一条return的时候可以省略
public class TestA {
public static void main(String[] args) {
//Lanbda标准形式实现接口
InterfaceA interfaceA = () -> {
System.out.println("Lambda表达式实现A接口");
};
InterfaceB interfaceB = () -> {
System.out.println("B接口中的方法");
};
InterfaceC interfaceC = (int a) -> {
System.out.println("C接口中的方法" + a);
};
InterfaceD interfaceD = (int a, int b) -> {
System.out.println("D接口中的方法" + a + b);
};
interfaceA.show();
interfaceB.showB();
interfaceC.showC(3);
interfaceD.showD(4, 4);
System.out.println("--------------------------------");
//开始进行简化
InterfaceB interfaceB2 = () ->
System.out.println("B2接口中的方法");
InterfaceC interfaceC2 = a ->
System.out.println("C2接口中的方法" + a);
InterfaceD interfaceD2 = (a, b) ->
System.out.println("D2接口中的方法" + a + " " + b);
interfaceB2.showB();
interfaceC2.showC(5);
interfaceD2.showD(6, 7);
System.out.println("----------------------------------");
showInfos("测试匿名内部类", new InterfaceD() {
public void showD(int a, int b) {
methD();
System.out.println(a + b);
}
});
showInfos("测试Lambda表达式", (a, b) -> {
// methD();
System.out.println(a + b);
});
}
public static void showInfos(String msg, InterfaceD d) {
System.out.println("信息是:" + msg + " " + d);
d.showD(1, 2);
}
}
@FunctionalInterface
interface InterfaceA {
void show();
}
interface InterfaceB {
void showB();
}
interface InterfaceC {
void showC(int a);
}
interface InterfaceD {
default void methD() {
System.out.println("InterfaceD.default");
}
void showD(int a, int b);
}
运行结果:
1.匿名内部类可以为任意接口创建实例,不管接口中包含多少个抽象方法,匿名内部类都可以实现,但Lambda表达式只能为函数式接口提供实例(即只能实现一个抽象方法)
2.匿名内部类不仅可以为接口创建实例,也可以为抽象类和普通类创建实例(一般不这样用),但是Lambda表达式只能为函数式接口提供实例
3.匿名内部类实现的抽象方法可以在方法体中调用接口默认方法(default),但是Lambda表达式不提供该功能,所以Lambda是一种函数式编程,不能完全替代匿名内部类的作用
包装类
包装类相当于基本数据类型的 “引用类型版” ,是序列化后的数据,传输效率高
包装类提供非常方便的方法,是一个特殊的引用类型,就算作为方法参数效果和值类型是一样的(传值而不是传址)
public class Demo {
//就算作为方法参数 效果和值类型是一样的(传值而不是传址)
public static void main(String[] args) {
Integer aInteger = 1;
Integer bInteger = 2;
new Demo().swapInteger(aInteger, bInteger);
System.out.println(aInteger);
System.out.println(bInteger);
}
public void swapInteger(Integer a, Integer b) {
System.out.println(a+"==="+b);
Integer tempInteger = a;
a = b;
b = tempInteger;
System.out.println(a+"==="+b);
}
}
- 数据的装箱和拆箱:
装箱:从基本数据类型转换到包装类类型
拆箱:从包装类类型转换为基本数据类型
在jdk1.5之前都是手动装箱和拆箱:
装箱:包装类类型 名字 = new 包装类类型(基本数据类型值);
拆箱:基本数据类型 名字 = 包装类类型变量.XXXvalue();
ps:XXX--> 对应的是基本数据类型
在jdk1.5之后都是自动装箱和拆箱:
装箱:包装类型 名字 = 基本数据类型的值;
拆箱:基本数据类型 名字 = 包装类类型的变量
基本数据类型之间存在兼容关系(互相存储):
例如 整数 和 小数 和 字符
基本类型所对应的包装类彼此之间没有任何关系
ps: 数值类型的包装类的父类 是 Number
拆箱所使用方法:
final类:Byte,Double,Float,Integer,Long,Short --> 间接说明了传值而不是传址(特殊的引用类型)
public class IntegerDemo {
public static void main(String[] args) {
//装箱和拆箱操作
//1.自动装箱和拆箱
int age = 12;//基本数据类型
Integer objAge= age;//装箱
int ageInt = objAge;//拆箱
System.out.println(objAge + objAge);//可以计算
//2.手动装箱和拆箱
Integer objAge1 = new Integer(age);
int age2 = objAge1.intValue();
//剩余的包装类和值类型使用方式一致
}
}
- Integer <--> String:
/*
* 转换的时候,String类型的值,必须是数值字符串 不能是其他字符串不然会出现异常
*/
public class IntegerDemo2 {
public static void main(String[] args) {
//将String转换为Integer
//1.Integer中的静态方法,参数是一个"数值型的字符串"
Integer integer = Integer.valueOf("123");
System.out.println(integer);
//不可以,出现异常NumberFormatException
// System.out.println(Integer.valueOf("123a"));
//2.在创建Integer对象的时候来转换,需要是一个"数值字符串"
Integer integer2 = new Integer("123");
System.out.println(integer2);
//将Integer转换为String类型
String str = integer + "";
System.out.println(str + str);
//将String类型转换为基本数据类型
Integer integer3 = Integer.valueOf("123");
int i4 = integer3;
//下面的写法比上边的好
String str2 = "12345";
int i5 = Integer.parseInt(str2);//ps:int这个位置可以替换为其他的数据类型
//特殊的包装类Boolean,只认true和false
//除他们两个之外,任何数据得到的结果都是false
Boolean boolean1 = new Boolean("String");
Boolean boolean2 = new Boolean("true");
System.out.println(boolean1);
System.out.println(boolean2);
}
}
- 包装类中的享元原则(享元模式):
public class IntegerDemo3 {
public static void main(String[] args) {
//Integer的equals方法重写过
/*
* public boolean equals(Object obj) {
* if (obj instanceof Integer) {
* return value == ((Integer)obj).intValue();
* }
* return false;
* }
*/
Integer integer1 = new Integer(123);
Integer integer2 = new Integer(123);
System.out.println(integer1 == integer2);//false 地址
System.out.println(integer1.equals(integer2));//true
System.out.println("--------------------------");
/*
* public static Integer valueOf(int i) {
* final int offset = 128;
* if (i >= -128 && i <= 127) { // must cache
* return IntegerCache.cache[i + offset];
* }
* return new Integer(i);
* }
*/
//因为Integer存在一个缓冲区 --> 享元原则(模式)
//默认[-128,127]之间所产生的数据,使用地址都是同一个(减少内存开销)
Integer integer3 = Integer.valueOf(123);
Integer integer4 = Integer.valueOf(123);
System.out.println(integer3 == integer4);//true
System.out.println(integer3.equals(integer4));//true
System.out.println("--------------------------");
Integer integer5 = 123;//自动装箱的底层实现就是Integer.valueOf(123);
Integer integer6 = 123;
System.out.println(integer5 == integer6);//true
System.out.println(integer5.equals(integer6));//true
System.out.println("--------------------------");
Integer integer7 = new Integer(250);
Integer integer8 = new Integer(250);
System.out.println(integer7 == integer8);//false 堆空间地址
System.out.println(integer7.equals(integer8));//true
System.out.println("--------------------------");
Integer integer9 = Integer.valueOf(250);//return new Integer(i);
Integer integer10 = Integer.valueOf(250);
System.out.println(integer9 == integer10);//false 不在范围内
System.out.println(integer9.equals(integer10));//true
System.out.println("--------------------------");
Integer integer11 = 250;//自动装箱的底层实现就是Integer.valueOf(123);
Integer integer12 = 250;
System.out.println(integer11 == integer12);//false
System.out.println(integer11.equals(integer12));//true
System.out.println("--------------------------");
Integer integer13 = new Integer(123);
Integer integer14 = 123;
System.out.println(integer13 == integer14);//false
}
}
ps:整数包装类:缓存区间:[-128,127] character:缓存:[0,127]
Integer 和 int 之间的区别:
Integer:引用类型,存储在堆中,初始值为null
Int:值类型,存储在栈中,初始值为0
Integer 提供些方便操作的方法来进行操作,集合中只能存储包装类型