系列文章目录
JavaSE进阶01:继承、修饰符
JavaSE进阶02:多态、抽象类、接口
JavaSE进阶03:内部类、Lambda表达式
JavaSE进阶04:API中常用工具类
JavaSE进阶05:包装类、递归、数组的高级操作、异常
JavaSE进阶06:Collection集合、迭代器、List、ArrayList、LinkedList
JavaSE进阶07:泛型、Set集合、TreeSet、二叉树、红黑树
JavaSE进阶08:HashSet、Map集合、HashMap、TreeMap、可变参数、不可变集合
JavaSE进阶09:Stream流、File类
JavaSE进阶10:IO流、字节流、字节缓冲流
JavaSE进阶11:字符流、字符缓冲流、转换流、对象操作流、Properties集合
JavaSE进阶12:多线程、线程同步、线程池
JavaSE进阶13:网络编程入门、UDP通信程序、TCP通信程序、日志、枚举
JavaSE进阶14:类加载器、反射
JavaSE进阶15:XML、注解、单元测试
JavaSE进阶扩充:JDK8 HashMap底层分析(了解)
JavaSE进阶扩充:JDK8 ArrayList线程安全问题和源码分析、集合常见面试题
Java进阶作业
文章目录
1:内部类
1.1 内部类概述(理解)
内部类:就是在一个类中定义一个类。举例:在一个类A的内部定义一个类B,类B就被称为内部类
内部类分类:
内部类在类中定义的位置不同,可以分为如下两种形式
- 在类的成员位置:成员内部类
- 在类的局部位置:局部内部类
内部类的访问特点:
- 内部类可以直接访问外部类的成员,包括私有
- 外部类要访问内部类的成员,必须创建对象
package lesson0409;
public class Outer {
private int num = 10;
public class Inner {
public void show() {
System.out.println(num);
}
}
public void method() {
// show();//外部类要访问内部类的成员,必须创建对象
Inner i = new Inner();
i.show();
}
}
class TestOuter{
public static void main(String[] args) {
Outer outer =new Outer();
outer.method();//10
}
}
1.2 成员内部类(理解)
成员内部类就是指在类的成员位置定义内部类。
成员内部类分为:
- 私有成员内部类
- 静态成员内部类
外界创建成员内部类格式:
- 格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
- 举例:Outer.Inner oi = new Outer().new Inner();
public class Outer {
//静态的成员内部列
public class Inner{
public void show(){
System.out.println("Inner..show");
}
}
}
class TestOuter{
public static void main(String[] args) {
//非私有内部列创建对象调用方法
Outer.Inner inner=new Outer().new Inner();
inner.show();
}
}
私有成员内部类
将一个类,设计为内部类的目的,大多数都是不想让外界去访问,所以内部类的定义应该私有化,私有化之后,再提供一个可以让外界调用的方法,方法内部创建内部类对象并调用。
私有成员内部类:只能在自己所在的外部类中创建对象访问
私有成员内部类例子:
public class Outer2 {
//私有的成员内部类的访问特点:只能在自己所在的外部类中创建对象访问
private static class Inner{
public void show1(){
System.out.println("private..Inner..show");
}
public static void show2(){
System.out.println("private..Inner..static..show2");
}
}
public void method(){
Inner inner=new Inner();
inner.show1();
Inner.show2();
}
}
class TestOuter2{
public static void main(String[] args) {
// Outer2.Inner inner=new Outer2.Inner();
// inner.show();
// inner.method();
// Outer2.Inner.method();
// //以上都报错,因为Inner为私有
Outer2 outer=new Outer2();
outer.method();
}
}
静态成员内部类
- 静态成员内部类访问格式:外部类名.内部类名 对象名 = new 外部类名.内部类名();
- 静态成员内部类中的静态方法:外部类名.内部类名.方法名();
静态成员内部类例子:
public class Outer {
//静态的成员内部列
public static class Inner{
public void show(){
System.out.println("Inner..show");
}
public static void show2(){
System.out.println("Inner..static..show2");
}
}
}
class TestOuter{
public static void main(String[] args) {
//非私有内部列创建对象调用方法
Outer.Inner inner=new Outer.Inner();
inner.show();
inner.show2();
//静态内部类的静态方法可以直接静态调用方法
Outer.Inner.show2();
}
}
1.3 局部内部类(理解)
- 局部内部类是在方法中定义的类,所以外界是无法直接使用,需要在方法内部创建对象并使用
- 该类可以直接访问外部类的成员,也可以访问方法内的局部变量
- 局部内部类能用的修饰符只有final和abstract。因为局部内部类是放在代码块或方法中的,不能有访问控制修饰符,且不能用static修饰。
public class Outer {
private int num = 10;
public void method() {
int num2 = 20;
class Inner {
public void show() {
System.out.println(num);//10
System.out.println(num2);//20
}
}
Inner i = new Inner();
i.show();
}
}
class OuterDemo {
public static void main(String[] args) {
Outer o = new Outer();
o.method();
}
}
1.4 匿名内部类(应用)
匿名内部类是局部内部类的一种特殊形式,所以它归属于局部内部类。
匿名内部类的理解:将 继承或实现、方法重写、创建对象 放在了一步进行。
匿名内部类前提:存在一个类或者接口,这里的类可以是具体类也可以是抽象类。
/*格式*/
new 类名or接口名(){
重写方法;
}
/*范例*/
new Inter(){
public void show(){
}
}
这里有new,说明最终结果应该是一个对象。但是如果这里是抽象类或者接口,它是不能创建对象的,那这个对象到底是什么对象呢?它里面重写了父类或者接口中的方法,所以,说明这里的对象应该是一个父类的子类对象或者接口的实现类对象。因为该实现类对象没有名字,所以叫做匿名内部类。
匿名内部类本质:是一个继承了该类的子类匿名对象,或者是一个实现了该接口的实现类匿名对象
/*接口*/
public interface InterMore {
public abstract void show1();
public abstract void show2();
}
public interface InterOnly {
public abstract void show3();
}
↓传统创建实现类对象↓
/*
传统实现接口或抽象类:
1.创建一个接口
2.创建一个实现类,通过imlements关键字去实现该接口
3.重写父类接口的抽象方法
4.创建实现类对象
5.调用重写后的方法
*/
/*实现类*/
public class InterImpl implements InterMore {
@Override
public void show1() {
System.out.println("InterImpl...show1");
}
@Override
public void show2() {
System.out.println("InterImpl...show2");
}
}
/*测试类*/
public class Anonymous {
public static void main(String[] args) {
System.out.println("↓传动实现类↓");
InterMore inter=new InterImpl();
inter.show1();
inter.show2();
}
↓匿名内部类单方法情况↓
public class Anonymous {
public static void main(String[] args) {
System.out.println("↓匿名内部类单方法情况↓");
new InterOnly(){
@Override
public void show3() {
System.out.println("InterOnly...show3");
}
}.show3();
}
↓匿名内部类多方法情况↓
public class Anonymous {
public static void main(String[] args) {
System.out.println("↓匿名内部类多方法情况↓");
InterMore interMore = new InterMore() {
@Override
public void show1() {
System.out.println("InterMore...show1");
}
@Override
public void show2() {
System.out.println("InterMore...show2");
}
};
interMore.show1();
interMore.show2();
}
}
1.5 匿名内部类在开发中的应用(应用)
当一个方法的参数是抽象类或接口时,使用匿名内部类非常方便。
/*游泳接口*/
public interface Swimming {
void goSwimming();
}
/*潜水接口*/
public interface Diving {
void goDiving();
}
/*测试类*/
public class TestAnoymous {
public static void goSV(Swimming swimming,Diving diving){
swimming.goSwimming();
diving.goDiving();
}
public static void main(String[] args) {
goSV(new Swimming() {
@Override
public void goSwimming() {
System.out.println("去游泳");
}
}, new Diving() {
@Override
public void goDiving() {
System.out.println("去潜水");
}
});
}
}
1.6 API 回顾
来,继续啊,学完了面向对象高级部分的知识后,下面呢我们来回顾一下前面学习过的JDK提供的几个API。
当时,不敢多看,是因为好多知识我们还没有学习,现在我们就可以去看看了。
我们重点看以下三个类:
- String
- SimpleDateFormat
- JFrame
接下来,依次来看:
1:String
格式问题:
equals方法的参数问题:多态
2:SimpleDateFormat
格式问题:
格式化和解析的方法问题:来自父类
3:JFrame
格式问题:
添加方法的问题:来自于继承体系,并且参数是多态的形式
方法参数为什么不直接是:JButton,JLabel,JTextField,JTextArea呢?多态
剩下两个,自己看。
通过刚才的API的回顾,我们发现了,JDK提供的API中处处体现了继承关系,实现关系,以及多态的应用。而这些内容只有我们学完了面向对象高级知识才能够看懂。当然,目前我们仅仅是从语法
层面对面向对象有了进一步的了解,而面向对象设计层面的知识,我们还需要再后续学习过程中,不断的去思考,去理解。特别是当我们后面做完一个实际开发的项目后,对面向对象我们可能才会
有一些比较深刻的体会。
好了,关于API的回顾我们就先讲到这里。
2:Lambda表达式
2.1 体验Lambda表达式(理解)
这个符号就叫做Lambda,它被称为函数式编程。
函数就是有输入量、输出量的一套计算方案,也就是“拿数据做操作”。
面向对象思想:匿名内部类实现,重点是做什么?
函数式思想:Lambda表达式实现,重点是哪个对象去怎么做事情?
public interface Inter {
void show();
}
/* 测试类 */
public class InterDemo {
public static void main(String[] args) {
//匿名内部类实现:强调怎么做?
useInter(new Inter() {
@Override
public void show() {
System.out.println("匿名内部类实现");
}
});
//Lambda表达式实现:强调做什么?
useInter(() -> {
System.out.println("Lambda表达式实现");
});
}
public static void useInter(Inter i) {
i.show();
}
}
2.2 Lambda表达式标准格式(理解)
Lambda表达式的使用前提:
- 有一个接口
- 接口中只能有一个抽象方法
Lambda表达式的三要素:形式参数,箭头,代码块
Lambda表达式的格式:
- 格式:(形式参数) -> {代码块}
- 形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可。
- ->:由英文中划线和大于符号组成,固定写法,代表指向动作。
- 代码块:是我们具体要做的事情,也就是以前我们写的方法体内容。
总结起来就是一句话:拿着形式参数去做代码块中的事情。
@FunctionalInterface //检查当前的借口是否是一个 函数式接口:1.接口 2.只有一个抽象方法
public interface Swimming{
public abstract void swim();
//以下都不是抽象方法,上面有且仅有一个抽象方法,@FunctionalInterface 不报错
public default void method(){
System.out.println("我是一个默认方法");
}
private void method2(){
System.out.println("我是一个私有方法");
}
static void method3(){
System.out.println("我是一个静态方法");
}
}
注意:
如果一个接口没有直接的父接口,name会将Object中的public方法在接口中隐式的声明一份,参数和返回值都一样。在接口中重写了父类Object类中任意一个public方法的方法并不算接口中的抽象方法。
@FunctionalInterface //不会报错,确定为函数式借口
public interface Inter{
public abstract boolean compare(inta,intb);
@Override//不报错,确定为重写
public abstract boolean equals(Object obj);//重写Obect中的equals方法,并不是Inter自己的方法
}
这个equals方法的具体实现来自于Object类,由此可以知道即使接口里面对equals方法进行了定义,实际上的实现还是Object方法中的,在Java语言规范中,如果一个接口没有父接口,那么就会将Object中的public方法在接口里隐含的申明一份,参数,返回值什么的都一样.
重写了父类Object类中任意一个public方法的方法并不算接口中的抽象方法。所以虽然Compare接口中有两个抽象方法compare和equals,但equals并不算接口中的抽象方法,所以Compare接口还是满足函数式接口的要求,Compare接口是一个函数式接口。
匿名内部类中重写show()方法的代码分析
- 方法形式参数为空,说明调用方法时不需要传递参数
- 方法返回值类型为void,说明方法执行没有结果返回
- 方法体中的内容,是我们具体要做的事情
Lambda表达式的代码分析 - ():里面没有内容,可以看成是方法形式参数为空
- ->:用箭头指向后面要做的事情
- { }:包含一段代码,我们称之为代码块,可以看成是方法体中的内容
2.3 练习1(无参无返回值)(应用)
public interface Eatable {
void eat();
}
/*
Lambda表达式的格式:(形式参数) -> {代码块}
练习1:
1:定义一个接口(Eatable),里面定义一个抽象方法:void eat();
2:定义一个测试类(EatableDemo),在测试类中提供两个方法
一个方法是:useEatable(Eatable e)
一个方法是主方法,在主方法中调用useEatable方法
*/
public class EatableDemo {
public static void main(String[] args) {
//在主方法中调用useEatable方法
//匿名内部类
useEatable(new Eatable() {
@Override
public void eat() {
System.out.println("一天一苹果,医生远离我");
}
});
//Lambda表达式
useEatable(() -> {
System.out.println("一天一苹果,医生远离我");
});
}
private static void useEatable(Eatable e) {
e.eat();
}
}
2.4 练习2(带参无返回值)(应用)
public interface Flyable {
void fly(String s);
}
/*
Lambda表达式的格式:(形式参数) -> {代码块}
练习2:
1:定义一个接口(Flyable),里面定义一个抽象方法:void fly(String s);
2:定义一个测试类(FlyableDemo),在测试类中提供两个方法
一个方法是:useFlyable(Flyable f)
一个方法是主方法,在主方法中调用useFlyable方法
*/
public class FlyableDemo {
public static void main(String[] args) {
//在主方法中调用useFlyable方法
//匿名内部类
useFlyable(new Flyable() {
@Override
public void fly(String s) {
System.out.println(s);
System.out.println("飞机自驾游");
}
});
System.out.println("--------");
//Lambda
useFlyable((String s) -> {
System.out.println(s);
System.out.println("飞机自驾游");
});
}
private static void useFlyable(Flyable f) {
f.fly("风和日丽,晴空万里");
}
}
2.5 练习3(带参带返回值)(应用)
public interface Addable {
int add(int x,int y);
}
/*
Lambda表达式的格式:(形式参数) -> {代码块}
练习3:
1:定义一个接口(Addable),里面定义一个抽象方法:int add(int x,int y);
2:定义一个测试类(AddableDemo),在测试类中提供两个方法
一个方法是:useAddable(Addable a)
一个方法是主方法,在主方法中调用useAddable方法
*/
public class AddableDemo {
public static void main(String[] args) {
//在主方法中调用useAddable方法
useAddable((int x,int y) -> {
return x + y;
// return x - y;
});
}
private static void useAddable(Addable a) {
int sum = a.add(10, 20);
System.out.println(sum);
}
}
2.6 Lambda的省略模式(应用)
省略规则:
- 参数类型可以省略。但是有多个参数的情况下,不能只省略一个
- 如果参数有且仅有一个,那么小括号可以省略
- 如果代码块的语句只有一条,可以省略大括号和分号,甚至是return。注意:要么都不省略,要么3个都要省略。
public interface Addable {
int add(int x, int y);
}
public interface Flyable {
void fly(String s);
}
/*
Lambda表达式的省略模式
*/
public class LambdaDemo {
public static void main(String[] args) {
// useAddable((int x,int y) -> {//原式
// return x + y;
// });
useAddable((x, y) -> {//参数的类型可以省略
return x + y;
});
// useAddable((x,int y) -> {//报错:但是有多个参数的情况下,不能只省略一个
// return x + y;
// });
// useFlyable((String s) -> {//原式
// System.out.println(s);
// });
// useFlyable((s) -> {//参数的类型可以省略
// System.out.println(s);
// });
// useFlyable(s -> {//如果参数有且仅有一个,那么小括号可以省略
// System.out.println(s);
// });
//如果代码块的语句只有一条,可以省略大括号和分号,必须都省掉
useFlyable(s -> System.out.println(s));
//如果代码块的语句只有一条,可以省略大括号和分号,如果有return,return也要省略掉,即必须都省掉。
useAddable((x, y) -> x + y);
}
private static void useFlyable(Flyable f) {
f.fly("风和日丽,晴空万里");
}
private static void useAddable(Addable a) {
int sum = a.add(10, 20);
System.out.println(sum);
}
}
2.7 Lambda和匿名内部类的区别(理解)
- 所需类型不同
- 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
- Lambda表达式:只能是接口
- 使用限制不同
- 如果接口中仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
- 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
- 实现原理不同
- 匿名内部类:编译之后,产生一个单独的.class字节码文件
- Lambda表达式:编译之后,你没有看到一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成
public interface Inter {
void show();
// void show2();
}
public abstract class Animal {
public abstract void method();
}
public class Student {
public void study() {
System.out.println("爱生活,爱Java");
}
}
/*
Lambda表达式和匿名内部类的区别
*/
public class LambdaDemo {
public static void main(String[] args) {
//匿名内部类
useInter(new Inter() {
@Override
public void show() {
System.out.println("接口");
}
});
useAnimal(new Animal() {
@Override
public void method() {
System.out.println("抽象类");
}
});
useStudent(new Student(){
@Override
public void study() {
System.out.println("具体类");
}
});
//Lambda
// useInter(() -> System.out.println("接口"));
// useAnimal(() -> System.out.println("抽象类"));
// useStudent(() -> System.out.println("具体类"));
// useInter(() -> System.out.println("接口"));
// useInter(new Inter() {
// @Override
// public void show() {
// System.out.println("show");
// }
//
// @Override
// public void show2() {
// System.out.println("show2");
// }
// });
}
private static void useStudent(Student s) {
s.study();
}
private static void useAnimal(Animal a) {
a.method();
}
private static void useInter(Inter i) {
i.show();
}
}