1.内部类-成员内部类
概念
- 一个类定义在另一个类里面,这个里面的类称为内部类,这个外面的类称为外部类
- 内部类也被称为嵌套类(inner class,nested class),密封类(sealed class)
格式
class Outer { //外部类
//成员位置 方法外
class Inner {} //内部类
public void method() {
//局部位置 方法内
class Inner2 {}
}
}
内部类的访问方式一:
通过外部类的方法使用内部类
public class Outer {
//内部类
class Inner {
public void show() {
System.out.println("show.....");
}
}
//方式一:通过外部类的方法使用
public void method() {
//创建内部类对象
Inner in = new Inner();
in.show();
}
}
内部类的访问方式二:
第三方直接创建内部类对象(格式)
//内部类访问方式二:第三方直接创建内部类对象
Outer.Inner oi = new Outer().new Inner();
oi.show();
内部类访问特点:
- 内部类可以直接访问外部类的成员,包括私有
- 外部类要访问内部类的成员,必须创建对象
2.私有成员内部类-静态成员内部类
成员内部类,也属于(成员),既然是成员就可以被一些修饰符所修饰
- private
私有成员内部类访问:在自己所在的外部类中创建对象访问。 - static
静态成员内部类访问格式:外部类名.内部类名 对象名 = new 外部类名.内部类名();
静态成员内部类中的静态方法:外部类名.内部类名.方法名();
内部类的分类:成员内部类(方法外定义) 、局部内部类(方法内定义)
成员内部类常见的修饰符
1.private
访问方式2种,少了一种
只能通过内部类的方法访问,第三方无法直接访问private的内部类
作用: 只希望在外部类内部使用,不希望第三方直接创建内部类对象
2.static
访问方式2种,格式变了
// Outer.Inner oi = new Outer().new Inner();//普通成员内部类方式不行
Outer.Inner oi = new Outer.Inner(); //少创建了一个外部类对象
作用:创建内部类对象更方便,无需创建外部类对象
如果静态成员内部类的成员是静态的,那么连内部类对象也可以不创建
public class Outer {
//内部类的分类
//private int age;
//成员内部类
// private class Inner {
// public void show() {
// System.out.println("show.....private");
// }
// }
static class Inner {
public void show() {
System.out.println("show.....static");
}
}
//方式一:通过外部类的方法使用
public void method() {
//创建内部类对象
Inner in = new Inner();
in.show();
}
}
public class Demo1 {
public static void main(String[] args) {
Outer outer = new Outer();
outer.method();
//第三方直接使用private成员内部类 ,No!!
// Outer.Inner oi = new Outer().new Inner();
// Outer.Inner oi = new Outer().new Inner();//普通成员内部类方式不行
Outer.Inner oi = new Outer.Inner(); //少创建了一个外部类对象
oi.show();
}
}
扩展
成员内部类经常用 private 修饰(ArrayList里有)
专门给外部类自己用, 其他类无法创建内部类对象
内部类的使用场景:
1. 有一个类,只想在本类内部使用,不希望其他类直接使用,可以声明为成员内部类,并使用 private 修饰
2. 一个类希望可以直接访问到其他类的 private 成员变量,可以声明为其内部类
3.局部内部类
扩展:
局部内部类访问局部变量,局部变量需要是常量
堆中的对象和局部变量的生命周期不一致
4.匿名内部类
好处:
如果接口的实现类(或者是父类的子类)只需要使用唯一的一次,
那么这种情况下就可以省略掉该类的定义,而改为使用“匿名内部类”。
public class Demo1 {
public static void main(String[] args) {
//创建实现类对象
new InterImpl().show();
//1.匿名内部类的前提
//1.有一个接口,抽象类
//2.格式
// new 接口名() {重写方法}
new Inter() {
@Override
public void show() {
System.out.println("匿名内部类实现show....");
}
}.show();
//多态调用
Inter in = new Inter() {
@Override
public void show() {
System.out.println("匿名内部类实现show....");
}
};
in.show();
in.show();
//3.格式的理解
// 一个实现了接口的实现类对象或继承了父类的子类对象
//4.好处
//如果一个实现类,只创建一次对象,就不用专门定义一个类了,可以用匿名内部类
}
}
interface Inter {
void show();
}
class InterImpl implements Inter {
@Override
public void show() {
System.out.println("实现类实现show....");
}
}
5.匿名内部类的使用场景
接口多态方式实现匿名内部类:
1. 写一个接口 interface Swiming
2. 写一个或多个实现类SwimingImpl,实现接口,重写抽象方法 swim()
3. 在测试类中,写一个方法,方法的参数类型是接口类型 useSwimming(Swiming swimming)
4. 在main方法中,调用方法,传入实现类对象,传入匿名内部类
示例:
public class Demo4 {
public static void main(String[] args) {
//传入接口实现类对象
useSwimming(new SwimmingImpl());
//传入匿名内部类
useSwimming(new Swimming() {
@Override
public void swim() {
System.out.println("匿名内部类..想游泳...");
}
});
}
//使用接口的方法
public static void useSwimming(Swimming swimming) {
swimming.swim();
}
}
interface Swimming {
void swim();
}
class SwimmingImpl implements Swimming {
@Override
public void swim() {
System.out.println("实现类..想游泳...");
}
}
使用匿名内部类的优势:
- 如果实现类对象只用一两次,就不用专门创建一个类了,可以使用匿名内部类
- 如果实现类对象需要经常使用,最好还是定义一个实现类实现接口,创建实现类对象
匿名内部类要点:
1.何时使用实现类或匿名内部类?
//匿名内部类: 1.如果接口实现类只使用1,2次 2.代码比较简单
//普通实现类: 1.如果接口实现类需要反复使用 2.代码较复杂
2. 格式
new 接口名/抽象类() {重写方法}
3. 格式的理解
实现类对象/子类对象
6.Lambda初体验和函数式编程思想
要点:
\1. 函数式编程思想
数学中的函数是有输入和输出的一套计算方案,简单说,就是拿数据做操作
例如:使用Excel,公式,求和,演示下
面向对象思想:
哪个对象去做? 做什么?
函数式编程思想:
更关注做什么,不关注哪个对象去做!
当要使用一个接口的时候,以前使用实现类对象,再使用匿名内部类,都是局限于面向对象的语法,需要传入一个对象,但其实,方法体才是我们真正要做的事!!!
2. 支持 Lambda 就是函数式编程思想在jdk8中的具体体现之一.
3. Lambda表达式可以简化面向对象的格式
7.Lambda表达式的格式说明和前提条件
要点:
Lambda的标准格式:
(参数列表) -> {代码}
例如: (int i) -> {System.out.println(i);}
试试,
1. 写一个接口 ShowHandler void show()
写个实现类 ShowHandlerImpl 实现接口,重写方法
2. 在测试类中,写一个方法,方法的参数类型是接口类型 void useShowHandler(ShowHandler showHandler)
3. 在main方法中,调用使用接口的方法,传入实现类对象,匿名内部类
4.使用Lambda替换匿名内部类
Lambda使用的前提:
1. 必须是接口,并且有且仅有一个抽象方法(函数式接口)
2. 必须有上下文环境
示例:
public class Demo1 {
public static void main(String[] args) {
// \1. 写一个接口 ShowHandler void show()
// \2. 在测试类中,写一个方法,方法的参数类型是接口类型 void useShowHandler(ShowHandler showHandler)
// \3. 在main方法中,调用使用接口的方法,传入匿名内部类
// \4.使用Lambda替换匿名内部类
useShowHandler(new ShowHandlerImpl());//实现类对象
//匿名内部类
useShowHandler(new ShowHandler() {
@Override
public void show() {
System.out.println("匿名内部类.show....");
}
});
//Lambda表达式 函数式编程思想(简化格式,实现功能)
useShowHandler(
() -> {
System.out.println("Lambda.show....");
});
}
//使用接口的方法
public static void useShowHandler(ShowHandler handler) {
handler.show();
}
}
//接口
interface ShowHandler {
public abstract void show();
}
//实现类
class ShowHandlerImpl implements ShowHandler {
@Override
public void show() {
System.out.println("实现类.show....");
}
}
小结:
面向对象思想:
强调 哪个对象? 做什么?
函数式编程思想:
不关注哪个对象,关注做什么? 方法体就是做什么的!
当要使用一个接口的时候,以前使用实现类对象,再使用匿名内部类,都是局限于面向对象的语法,但其实,方法体才是我们真正要做的事!
方法的参数是接口
1. 可以传入实现类对象
2. 可以传入匿名内部类
3. 可以传入Lambda( 必须是接口(有且仅有一个抽象方法))
8.Lambda的省略格式
9.匿名内部类和Lambda表达式的区别
Lambda使用前提:
-
是一个接口,有且仅有一个抽象方法(函数式接口)
-
要有上下文推断
Lambda小结:
lambda格式 (int x,int y) -> {}
Lambda好处 简化匿名内部类(必须是函数式接口:有且仅有一个抽象方法的接口)
Lambda的使用前提 1.函数式接口(有且仅有一个抽象方法的接口) 2.上下文环境
案例: 接口,抽象方法: 空参,有参,有返回值,有参有返回值
案例步骤:(1.定义接口, 2. 定义一个使用接口的方法,参数类型是接口 3.调用方法,传入匿名内部类或Lambda)
Lambda的省略规则
Lambda与匿名内部类的区别(1.函数式接口 2.原理)
API
1.API-基本使用
2.API-Math
产生1-100之间的int随机数
Math.random();
// 1- 100
Random r = new Random();
int num = r.nextInt(100) + 1;
// 1-100
```java
int n = (int) (Math.random() * 100) + 1;
3.API-System
要点:
1.简单介绍下,当前时间毫秒值的含义
1970-1-1 08:00:00 到现在过了多少个毫秒
long now = System.currentTimeMillis();
System.out.println(now);//1604314106909 当前时间毫秒值
System.out.println(now / 1000 / 60 / 60 / 24 / 365);//1970年到现在过了多少年
-
作用: 统计一段代码的运行时间
-
数组复制,索引不要越界
int[] arr = {1,2,3,4,5};
int[] arr2 = new int[5];
System.arraycopy(arr,0,arr2, 0,arr.length);//OK
System.arraycopy(arr,0,arr2,4,arr.length);//?? 索引不要越界
System.out.println(Arrays.toString(arr2));//打印数组元素
4.Object-toString
- Object类是所有类的直接或间接父类
- 直接打印一个对象就是打印这个对象的toString方法的返回值
- Object类的toString方法得到的是对象的地址值
- 我们一般会对toString方法进行重写
要点(idea操作):
1.Object是类层次结构的根类.任何类都直接或间接继承Object类,包括数组
class Animal {}
//class Animal extends Object {}
class Cat extends Animal {}
2.打印一个对象,其实就是打印对象的toString()返回值
Person p = new Person("jack",19);
System.out.println(p); //和下面效果一样
System.out.println(p.toString());//和上面效果一样
注意:
//注意: p 和 p.toString() 不是一回事
// 只是 System.out.println(p); 和 System.out.println(p.toString());效果一样
// System.out.println(p == p.toString()); //都不是一个类型,不能使用==比较
3.Object类的toString()方法:默认返回的是对象的地址值,一般没什么意义,我们一般会重写toString()返回对象成员 变量的字符串形式,使用快捷键 alt + insert自动生成即可
双击shift,输入Object,可以打开这个类的源码
5.Object-equals
回顾:
==:运算符
int i = 10;
int j = 10;
System.out.println(i == j);
String s = new String("hello");
String s2 = new String("hello");
System.out.println( s == s2);// false
Object类的equals()方法:是比较对象的地址值(意义不大),一般建议子类重写equals方法,比较对象的成员变量值,也就是比较对象的内容.
Person p = new Person("Jack",19);
Person p2 = new Person("Jack",19);
System.out.println(p == p2);//
System.out.println(p.equals(p2)); //不重写就调用Object类的equals()