【Java之旅】12. 深浅拷贝、内部类、lambda表达式
12. 1 深浅拷贝
① 浅拷贝
我们先看代码
public class Client {
public static void main(String[] args) {
Players p1 = new Players(1,"kupurk");
Players p2 = p1;
System.out.println("p1 = " + p1);
System.out.println("p2 = " + p2);
}
}
输出结果
p1 = Players{id=1, name='kupurk'}
p2 = Players{id=1, name='kupurk'}
我们构造了一个对象,用p1引用指向这个对象,我们再给p2赋于这个对象的地址,这便是浅拷贝,
但又由于两个引用指向的是同一个对象的原因,所以我们不好对 对象 进行修改。
② 深拷贝
如果我们想要两个对象,将其中一个对象的所有属性复制到另一个对象上,
这样就好对 对象 进行修改了,因此就有了深拷贝
深拷贝可以有两种方法
(1) clone()
③ 总结
- 浅拷贝:
- 深拷贝
12. 2 内部类
内部类顾名思义,是一个类内部中的类,如:
public class OuterClass
{
class InnerClass
{
;
}
}
这种在一个类中定义另一个类的形式便是内部类,内部类分四种:成员内部类、静态内部类、局部内部类、匿名内部类。
① 成员内部类
主类:
package study.InnerClass;
public class Client2 {
public static void main(String[] args) {
OuterClass.InnerClass in1 = new OuterClass().new InnerClass();
in1.say();
OuterClass out = new OuterClass();
OuterClass.InnerClass in2 = out.new InnerClass();
in2.say();
}
}
内部类与外部类:
package study.InnerClass;
public class OuterClass
{
class InnerClass
{
public InnerClass()
{
System.out.println("内部类构造!");
}
void say()
{
System.out.println("内部类重写调用say!!");
}
}
}
输出结果:
内部类构造!
内部类重写调用say!!
内部类构造!
内部类重写调用say!!
我们可以发现有两种方式可以定义构造内部类
1、 OuterClass.InnerClass in1 = new OuterClass().new InnerClass();
2、 OuterClass out = new OuterClass();
OuterClass.InnerClass in2 = out.new InnerClass();
这两种方法都可以构造内部类,前提都是先对外部类进行构造以后,才可对内部类进行构造。
②静态内部类
静态内部类和成员内部类类似,也是一个类中的属性,只不过是静态的属性。
而静态内部类也有许多条件
- 非静态内部类不能拥有静态成员,静态内部类可以拥有静态成员和非静态成员
package study.InnerClass;
public class OuterClass
{
class InnerClass1
{
static int a;
int b;
static void m1()
{}
void m2()
{}
}
static class InnerClass2
{
int a;
static int b;
static void m1()
{}
void m2()
{}
}
}
编译报错,这个普通的内部类的变量不能有静态修饰
而下面的静态内部类InnerClass2没有报错
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CtOezdGG-1614924239947)(C:\Users\37484\AppData\Roaming\Typora\typora-user-images\image-20210206024819293.png)]
- 静态内部类的静态成员可以访问外部类的静态变量,不能访问外部类的非静态成员
- 静态内部类的非静态成员可以访问外部类的静态变量和非静态成员
- 外部类访问非静态内部类的成员
③ 局部内部类
在类中的方法中定义类,并且构造这个类,这个内部类变为局部内部类
局部内部类的声明周期很短,出了那个外部类,就结束了。
package study.InnerClass;
public class Client2 {
public static void main(String[] args) {
OuterClass kupurk = new OuterClass();
kupurk.run();
}
}
public class OuterClass
{
int a;
public void run()
{
class InnerClass{
void fun()
{
System.out.println("局部内部类启动");
}
}
InnerClass p1 = new InnerClass();
p1.fun();
}
}
从代码中我们可以看到,我们是在方法中定义的一个内部类,这便是局部内部类。
他的生命周期只存在于那个方法的范围内。
但正应如此,我们不必要对方法中的局部内部类去命名,于是便诞生了匿名内部类
④ 匿名内部类
匿名内部类可以使你的代码更加简洁,我们可以在定义一个类的同时对其进行实例化。
它与局部类很相似,不同的是它没有类名,如果某个局部类只需要用一次,那么我们就可以使用匿名内部类.
以刚刚的局部内部类为例,用匿名内部类写一个与他等价的代码
第一种调用方法:将匿名内部类写在代码块中
package study.InnerClass;
public class Client2 {
public static void main(String[] args) {
OuterClass kupurk = new OuterClass();
kupurk.run();
}
}
public interface IRun {
void fun();
}
public class OuterClass {
public void run()
{
new IRun(){
public void fun(){
System.out.println("内部类启动");
}
}.fun();
}
}
主要 看最后一段代码
我们在run方法中 new了一个对象,这个对象所对应的类是没有名字的,因此叫匿名内部类
new [接口名或父类名](){
重写实现方法
...
}.调用方法名();
第二种调用方法:将匿名内部类写在方法参数中
package study.InnerClass;
public class Client2 {
public static void main(String[] args) {
OuterClass kupurk = new OuterClass();
kupurk.run(new IRun() {
@Override
public void fun() {
System.out.println("匿名内部类参数调用");
}
});
}
}
public interface IRun {
void fun();
}
public class OuterClass {
int a;
public void run(IRun canshu)
{
canshu.fun();
}
}
主要看main方法中的调用,我们在参数列表中定义声明了一个内部类
lambda表达式
上述调用匿名内部类的方法可能过于复杂,使用的时候会比较繁琐,在java1.8后便更新了一个新的特性
即 lambda表达式
还是以上述为例子,我们再继续对匿名内部类的写法进行简化
package study.InnerClass;
public class Client2 {
public static void main(String[] args) {
OuterClass kupurk = new OuterClass();
kupurk.run(()->System.out.println("lambda表达式简化匿名内部类"));
}
}
public interface IRun {
void fun();
}
public class OuterClass {
public void run(IRun canshu)
{
canshu.fun();
}
}
我们在main方法中省去了一大串的代码,即可得到相同的结果
还有带参数的lambda表达式
package study.InnerClass;
public class Client2 {
public static void main(String[] args) {
OuterClass kupurk = new OuterClass();
kupurk.run(str->System.out.println(str+"吃饭"),"kupurk");
}
}
public interface IRun {
void fun(String name);
}
public class OuterClass {
public void run(IRun canshu,String name)
{
canshu.fun(name);
}
}
使用lamda表达式的条件:
函数式接口(只含有一个抽象方法的接口)