一. 面向对象
面向对象和面向过程的思想对比 :
面向过程 :是一种以过程为中心的编程思想,实现功能的每一步,都是自己实现的。
优点:性能比面向对象高,因为类调用时需 要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
缺点:没有面向对象易维护、易复用、易扩展
面向对象 :是一种以对象为中心的编程思想,通过指挥对象实现具体的功能。
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护
缺点:性能比面向过程低
1.1 类和对象
1.1.1 类
类的组成是由属性和行为两部分组成
- 属性:在类中通过成员变量来体现(类中方法外的变量)
- 行为:在类中通过成员方法来体现(和前面的方法相比去掉static关键字即可)
1.1.2 对象
- 创建对象的格式:
- 类名 对象名 = new 类名();
- 调用成员的格式:
- 对象名.成员变量
- 对象名.成员方法();
1.1.3 类和对象的关系
客观存在的事物皆为对象 ,所以我们也常常说万物皆对象。
- 类
- 类的理解
- 类是对现实生活中一类具有共同属性和行为的事物的抽象
- 类是对象的数据类型,类是具有相同属性和行为的一组对象的集合
- 简单理解:类就是对现实事物的一种描述
- 类的组成
- 属性:指事物的特征,例如:手机事物(品牌,价格,尺寸)
- 行为:指事物能执行的操作,例如:手机事物(打电话,发短信)
- 类的理解
- 类和对象的关系
- 类:类是对现实生活中一类具有共同属性和行为的事物的抽象
- 对象:是能够看得到摸的着的真实存在的实体
- 简单理解:类是对事物的一种描述,对象则为具体存在的事物
总结: 多个对象在堆内存中,都有不同的内存划分,成员变量存储在各自的内存区域中,成员方法多个对象共用的 一份
总结 : 当多个对象的引用指向同一个内存空间(变量所记录的地址值是一样的) 只要有任何一个对象修改了内存中的数据,随后,无论使用哪一个对象进行数据获取,都是修改后的数据。
1.1.4 成员变量和局部变量
在程序执行的过程中,其值可以在某个范围内发生改变的量。从本质上讲,变量其实是内存中的一小块区域
- 类中位置不同:成员变量(类中方法外)局部变量(方法内部或方法声明上)。
- 内存中位置不同:成员变量(堆内存)局部变量(栈内存)。
- 生命周期不同:成员变量(随着对象的存在而存在,随着对象的消失而消失)局部变量(随着方法的调用而 存在,随着方法的调用完毕而消失)。
- 初始化值不同:成员变量(有默认初始化值)局部变量(因其在栈内存中,没有默认初始化值,必须先定义,赋值才能使用)。
成员变量包括实例变量和静态变量:
实例变量:独立于与方法之外的变量,无static修饰,声明在一个类中,但在方法、构造方法和语句块之外,
静态变量:独立于方法之外的变量,用static修饰。随着类的加载而加载
1.1.5 访问修饰符 public,private,protected,以及不写(默认)时的区别
定义:Java中可以使用访问修饰符来保护对类、变量、方法的访问。Java 支持 4 种不同的访问权限。
分类
private : 在同一类内可见。使用对象:变量、方法。注意:不能修饰类(外部类)
default (即缺省,什么也不写,不使用任何关键字): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。注意:不能修饰类(外部类)。
public : 对所有类可见。使用对象:类、接口、变量、方法
访问修饰符图
修饰符 | 当前类 | 同一包下 | 子类 | 其他包 |
private | 🐕 | |||
default | 🐕 | 🐕 | ||
protected | 🐕 | 🐕 | 🐕 | |
public | 🐕 | 🐕 | 🐕 | 🐕 |
1.1.6 this、super关键字的用法
this是指向对象本身的一个指针。
this的用法在java中大体可以分为3种:
- 普通的直接引用,this相当于是指向当前对象本身。
- this修饰的变量用于指代成员变量,其主要作用是(区分局部变量和成员变量的重名问题) 方法的形参如果与成员变量同名,不带this修饰的变量指的是形参,而不是成员变量 方法的形参没有与成员变量同名,不带this修饰的变量指的是成员变量
- 引用本类的构造函数
super可以理解为是指向自己父类对象的一个指针,而这个超类指的是离自己最近的一个父类。
super也有三种用法:
- 普通的直接引用,super相当于是指向当前对象的父类,这样就可以用super.xxx来引用父类的成员。
- 当子类中的成员变量或方法与父类中的成员变量或方法同名时,用super进行区分
- 引用父类构造函数
1.2 面向对象三大特性
抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括属性抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。
Java 面向对象编程三大特性:封装 继承 多态
封装:封装是把一个对象的属性私有化,隐藏内部的实现细节,同时提供一些可以被外界访问属性的方法(get和set)。通过封装可以使程序便于使用,提高复用性和安全性
继承:继承是使用已存在的类的定义作为基础,建立一个新的类,在添加新类的时候可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过继承可以提高代码复用性。继承是多态的前提。
关于继承如下 3 点请记住
- 子类拥有父类非 private 的属性和方法。
- 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
- 子类可以用自己的方式实现父类的方法。
多态性:父类或接口定义的引用变量可以指向子类或具体实现类的实例对象。多态提高了程序的扩展性。一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在程序运行期间才能决定。
Java实现多态有三个必要条件:继承、重写、向上转型。
- 继承:在多态中必须存在有继承关系的子类和父类。
- 重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
- 向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备调用父类和子类的方法的技能。
只有满足了上述三个条件,我们才能够在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而达到执行不同的行为。
1.2.1 继承
继承的特点:
- Java中类只支持单继承,不支持多继承
- Java中类支持多层继承
- 在子类方法中访问一个变量,采用的是就近原则。
- 非
1.2.1.1 继承中构造方法的访问特点
注意:子类中所有的构造方法默认都会访问父类中无参的构造方法
子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化,原因在于,每一个子类构造 方法的第一条语句默认都是:super()
问题:如果父类中没有无参构造方法,只有带参构造方法,该怎么办呢?
1. 通过使用super关键字去显示的调用父类的带参构造方法
2. 子类通过this去调用本类的其他构造方法,本类其他构造方法再通过super去手动调用父类的带参的构造方法
注意: this(…)super(…) 必须放在构造方法的第一行有效语句,并且二者不能共存
1.2.1.2 继承中成员方法的访问特点
通过子类对象访问一个方法
- 子类成员范围找
- 父类成员范围找
1.2.1.3 super内存图
- 对象在堆内存中,会单独存在一块super区域,用来存放父类的数据
1.2.1.4 方法重写
- 1、方法重写概念
- 子类出现了和父类中一模一样的方法声明(方法名一样,参数列表也必须一样)
- 2、方法重写的应用场景
- 当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容
- 3、Override注解
- 用来检测当前的方法,是否是重写的方法,起到【校验】的作用
1.2.1.5 方法重写的注意事项
- 方法重写的注意事项
- 私有方法不能被重写(父类私有成员子类是不能继承的)
- 子类方法访问权限不能更低(public > 默认 > 私有)
- 静态方法不能被重写,如果子类也有相同的方法,并不是重写的父类的方法
1.2.2 多态
什么是多态: 同一个对象,在不同时刻表现出来的不同形态
1.2.2.1 多态的前提
-
- 要有继承或实现关系
- 要有方法的重写
- 要有父类引用指向子类对象
- 代码演示
class Animal {
public void eat(){
System.out.println("动物吃饭");
}
}
class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
public class Test1Polymorphic {
/*
多态的前提:
1. 要有(继承 \ 实现)关系
2. 要有方法重写
3. 要有父类引用, 指向子类对象
*/
public static void main(String[] args) {
// 当前事物, 是一只猫
Cat c = new Cat();
// 当前事物, 是一只动物
Animal a = new Cat();
a.eat();
}
}
1.2.2.2 成员访问特点:
- 成员变量 编译看父类,运行看父类
- 成员方法 编译看父类,运行看子类
1.2.2.3 多态的好处和弊端:
- 好处 提高程序的扩展性。定义方法时候,使用父类型作为参数,在使用的时候,使用具体的子类型参与操作
- 弊端 不能使用子类的特有成员
1.2.2.4 多态中的转型:
- 向上转型 父类引用指向子类对象就是向上转型
- 向下转型 格式:子类型 对象名 = (子类型)父类引用;
class Fu { public void show(){ System.out.println("Fu..show..."); } } class Zi extends Fu { @Override public void show() { System.out.println("Zi..show..."); } public void method(){ System.out.println("我是子类特有的方法, method"); } } public class Test3Polymorpic { public static void main(String[] args) { // 1. 向上转型 : 父类引用指向子类对象 Fu f = new Zi(); f.show(); // 多态的弊端: 不能调用子类特有的成员 // f.method(); // A: 直接创建子类对象 // B: 向下转型 // 2. 向下转型 : 从父类类型, 转换回子类类型 Zi z = (Zi) f; z.method(); } }
1.2.2.5 多态中转型存在的风险和解决方案:
风险如果被转的引用类型变量,对应的实际类型和目标类型不是同一种类型,那么在转换的时候就会出现ClassCastException
- 解决方案
- 关键字instanceof
- 使用格式:
-
变量名 instanceof 类型
通俗的理解:判断关键字左边的变量,是否是右边的类型,返回boolean类型结果
abstract class Animal { public abstract void eat(); } class Dog extends Animal { public void eat() { System.out.println("狗吃肉"); } public void watchHome(){ System.out.println("看家"); } } class Cat extends Animal { public void eat() { System.out.println("猫吃鱼"); } } public class Test4Polymorpic { public static void main(String[] args) { useAnimal(new Dog()); useAnimal(new Cat()); } public static void useAnimal(Animal a){ // Animal a = new Dog(); // Animal a = new Cat(); a.eat(); //a.watchHome(); // Dog dog = (Dog) a; // dog.watchHome(); // ClassCastException 类型转换异常 // 判断a变量记录的类型, 是否是Dog if(a instanceof Dog){ Dog dog = (Dog) a; dog.watchHome(); } } }
1.2.3 抽象类
当我们在做子类的共性功能提取时,有些方法在父类中并没有具体的体现,只在父类给出了抽象的类名.
抽象类的特点:
- 抽象类和抽象方法必须使用 abstract 关键字修饰
//抽象类的定义 public abstract class 类名 {} //抽象方法的定义 public abstract void eat();
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
- 抽象类不能实例化
- 抽象类可以有构造方法
- 抽象类的子类 要么重写抽象类中的所有抽象方法 要么是抽象类
1.3 分类思想
1.3.1 分类思想概述 (理解)
分工协作,专人干专事
1.3.2 常用分类
- pojo类(实体类) :Student类 标准学生类,封装键盘录入的学生信息(id , name , age , birthday)
- StudentDao类 : Dao : (Data Access Object 缩写) 用于访问存储数据的数组或集合
- Service类 : 用来进行业务逻辑的处理(例如: 判断录入的id是否存在)
- Controller类 : 和用户打交道(接收用户需求,采集用户信息,打印数据到控制台)
1.4.分包思想
1.4.1 分包思想概述
如果将所有的类文件都放在同一个包下,不利于管理和后期维护,所以,对于不同功能的类文件,可以放在不同的包下进行管理
1.4.2 包的概述 (记忆)
- 包本质上就是文件夹
- 创建包多级包之间使用 " . " 进行分割 多级包的定义规范:公司的网站地址翻转(去掉www) 比如:百度的网站址为www.baidu.com 后期我们所定义的包的结构就是:com.百度.其他的包名
- 包的命名规则字母都是小写
3.3包的注意事项 (理解)
- package语句必须是程序的第一条可执行的代码
- package语句在一个java文件中只能有一个
- 如果没有package,默认表示无包名
3.4类与类之间的访问 (理解)
- 同一个包下的访问不需要导包,直接使用即可
- 不同包下的访问
- import 导包后访问
- .通过全类名(包名 + 类名)访问
- 注意:import 、package 、class 三个关键字的摆放位置存在顺序关系:package 必须是程序的第一条可执行的代码,import 需要写在 package 下面,class 需要在 import 下面
二. static存在的主要意义
static的主要意义是在于创建独立于具体对象的变量或者方法。即使没有创建对象,也能使用属性和调用方法!
static修饰的特点 (记忆) :
- 被类的所有对象共享,是我们判断是否使用静态关键字的条件
- 随着类的加载而加载,优先于对象存在,而对象需要类被加载后,才能创建
- 可以通过类名调用,也可以通过对象名调用
- 用来形成静态代码块来优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。因此,很多时候会将一些只需要进行一次初始化的操作放在static代码块中。
static关键字注意事项 (理解):
- 静态方法只能访问静态的成员
- 非静态方法可以访问静态的成员,也可以访问非静态的成员
- 静态方法中是没有this关键字
三 . final finally finalize意义
- final是一个修饰符关键字,可以修饰类、方法、变量,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表示该变量是一个常量不能被重新赋值。
变量是基本类型,不能改变的是值 变量是引用类型,不能改变的是地址值,但地址里面的内容是可以改变的 public static void main(String[] args){ final Student s = new Student(23); s = new Student(24); // 错误 s.setAge(24); // 正确 }
- finally是一个异常处理的关键字,一般作用在try-catch-finally代码块中,在处理异常的时候,通常我们将一定要执行的代码放在finally代码块中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。
- finalize是属于Object类的一个方法,该方法一般由垃圾回收器来调用,它的设计目的是保证对象在被垃圾收集前完成特定资源的回收。finalize 机制现在已经不推荐使用,并且在 JDK 9 被标记为 deprecated。
四. 代码块
- 局部代码块
public class Test { /* 局部代码块 位置:方法中定义 作用:限定变量的生命周期,及早释放,提高内存利用率 */ public static void main(String[] args) { { int a = 10; System.out.println(a); } // System.out.println(a); } }
- 构造代码块
public class Test { /* 构造代码块: 位置:类中方法外定义 特点:每次构造方法执行的时,都会执行该代码块中的代码,并且在构造方法执行前执行 作用:将多个构造方法中相同的代码,抽取到构造代码块中,提高代码的复用性 */ public static void main(String[] args) { Student stu1 = new Student(); Student stu2 = new Student(10); } } class Student { { System.out.println("好好学习"); } public Student(){ System.out.println("空参数构造方法"); } public Student(int a){ System.out.println("带参数构造方法..........."); } }
五 . 接口
- Java中接口存在的两个意义
-
- 用来定义规范
- 用来做功能的拓展
5.1 接口的特点
- 接口用关键字interface修饰 public interface 接口名 {}
- 类实现接口用implements表示 public class 类名 implements 接口名 {}
- 接口不能实例化 我们可以创建接口的实现类对象使用
- 接口的子类 要么重写接口中的所有抽象方法 要么子类也是抽象类
5.2 接口的成员特点
- 成员特点
- 成员变量 只能是常量 默认修饰符:public static final
- 构造方法 没有,因为接口主要是扩展功能的,而没有具体存在
- 成员方法 只能是抽象方法 默认修饰符:public abstract 关于接口中的方法,JDK8和JDK9中有一些新特性,
- 代码演示
- 接口
public interface Inter { public static final int NUM = 10; public abstract void show(); }
- 实现类
class InterImpl implements Inter{ public void method(){ // NUM = 20; System.out.println(NUM); } public void show(){ } }
- 测试类
public class TestInterface { /* 成员变量: 只能是常量 系统会默认加入三个关键字 public static final 构造方法: 没有 成员方法: 只能是抽象方法, 系统会默认加入两个关键字 public abstract */ public static void main(String[] args) { System.out.println(Inter.NUM); InterImpl interImpl=new InterImpl; interImpl.method; } }
5.3 类和接口的关系
- 类与类的关系(亲爹)
-
继承关系,只能单继承,但是可以多层继承
- 类与接口的关系(干爹)
-
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
- 接口与接口的关系
继承关系,可以单继承,也可以多继承
5.4 接口中默认方法【应用】
主要用于解决接口升级问题
- 格式
-
public default 返回值类型 方法名(参数列表) {}
作用解决接口升级的问题
- 范例
-
public default void show3() { }
- 注意事项
- 默认方法不是抽象方法,所以不强制被重写。但是可以被重写,重写的时候去掉default关键字
- public可以省略,default不能省略
- 如果实现了多个接口,多个接口中存在相同的方法声明,子类就必须对该方法进行重写
5.5 接口中静态方法
- 格式
-
public static 返回值类型 方法名(参数列表) { }
- 范例
-
public static void show() {}
- 注意事项
- 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
- public可以省略,static不能省略
5.6 接口中私有方法【应用】
私有方法产生原因Java 9中新增了带方法体的私有方法,这其实在Java 8中就埋下了伏笔:Java 8允许在接口中定义带方法体的默认方法和静态方法。这样可能就会引发一个问题:
当两个默认方法或者静态方法中包含一段相同的代码实现时,程序必然考虑将这段实现代码抽取成一个共性方法,而这个共性方法是不需要让别人使用的,因此用私有给隐藏起来,这就是Java 9增加私有方法的必然性
- 定义格式
- 格式1:
-
private 返回值类型 方法名(参数列表) { }
private void show() { }
-
- 格式2:
- 注意事项
- 默认方法可以调用私有的静态方法和非静态方法
- 静态方法只能调用私有的静态方法
-
private static 返回值类型 方法名(参数列表) { }
private static void method() { }
六 .内部类
6.1 内部类的概述
内部类概念
在一个类中定义一个类。举例:在一个类A的内部定义一个类B,类B就被称为内部类
/*
格式:
class 外部类名{
修饰符 class 内部类名{
}
}
*/
class Outer {
public class Inner {
}
}
内部类的访问特点 :
/*
内部类访问特点:
内部类可以直接访问外部类的成员,包括私有
外部类要访问内部类的成员,必须创建对象
*/
public class Outer {
private int num = 10;
public class Inner {
public void show() {
System.out.println(num);
}
}
public void method() {
Inner i = new Inner();
i.show();
}
}
6.2 内部类分类
6.2.1 成员内部类
- 成员内部类的定义位置
- 在类中方法,跟成员变量是一个位置
- 外界创建成员内部类格式
- 格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
- 举例:Outer.Inner oi = new Outer().new Inner();
-
- 将一个类,设计为内部类的目的,大多数都是不想让外界去访问,所以内部类的定义应该私有化,私有化之后,再提供一个可以让外界调用的方法,方法内部创建内部类对象并调用。
- 示例代码:
class Outer { private int num = 10; private class Inner { public void show() { System.out.println(num); } } public void method() { Inner i = new Inner(); i.show(); } } public class InnerDemo { public static void main(String[] args) { //Outer.Inner oi = new Outer().new Inner();错误写法 //oi.show(); Outer o = new Outer(); o.method(); } }
静态成员内部类:
-
- 静态成员内部类访问格式:外部类名.内部类名 对象名 = new 外部类名.内部类名();
- 静态成员内部类中的静态方法:外部类名.内部类名.方法名();
- 示例代码
- --访问外部类的指定成员 outer.this
--访问外部类的父类指定成员 super.this
class Outer {
static class Inner {
public void show(){
System.out.println("inner..show");
}
public static void method(){
System.out.println("inner..method");
}
}
}
public class Test3Innerclass {
/*
静态成员内部类演示
*/
public static void main(String[] args) {
// 外部类名.内部类名 对象名 = new 外部类名.内部类名();
Outer.Inner oi = new Outer.Inner();
oi.show();
Outer.Inner.method();
//test.Inner.show(); 错误的写法,必须要show为静态方法才可以
}
}
6.2.2 局部内部类
- 局部内部类定义位置
- 局部内部类是在方法中定义的类
- 局部内部类方式方式
- 局部内部类,外界是无法直接使用,需要在方法内部创建对象并使用
- 该类可以直接访问外部类的成员,也可以访问方法内的局部变量
示例代码
class Outer {
private int num = 10;
public void method() {
int num2 = 20;
class Inner {
public void show() {
System.out.println(num);
System.out.println(num2);
}
}
Inner i = new Inner();
i.show();
}
}
public class OuterDemo {
public static void main(String[] args) {
Outer o = new Outer();
o.method();
}
}
6.2.3 匿名内部类
- 匿名内部类的前提:
-
存在一个类或者接口,这里的类可以是具体类也可以是抽象类
- 匿名内部类的格式:
-
new 类名 ( ) { 重写方法 }
new 接口名 ( ) { 重写方法 }
-
- 匿名内部类的理解:将继承/实现,方法重写,创建对象,放在了一步进行
- 解释:实现了接口的,一个实现类对象
-
举例:
new Inter(){ @Override public void method(){} }
}
- 匿名内部类的本质: 是一个继承了该类或者实现了该接口的子类匿名对象
- 匿名内部类的细节: 匿名内部类可以通过多态的形式接受
Inter i = new Inter(){ @Override public void method(){ } }
匿名内部类直接调用方法
interface Inter{
void method();
}
class Test{
public static void main(String[] args){
new Inter(){
@Override
public void method(){
System.out.println("我是匿名内部类");
}
}.method(); // 直接调用方法
}
}
6.2.4 匿名内部类在开发中的使用
- 匿名内部类在开发中的使用
- 当发现某个方法需要,形参是接口或抽象类的子类对象,我们就可以传递一个匿名内部类过去,来简化传统的代码
- 示例代码:
//idea中打出new加空格 自动提示
/*
游泳接口
*/
interface Swimming {
void swim();
}
public class TestSwimming {
public static void main(String[] args) {
goSwimming(new Swimming() {
@Override
public void swim() {
System.out.println("铁汁, 我们去游泳吧");
}
});
}
/**
* 使用接口的方法
*/
public static void goSwimming(Swimming swimming){
/*
Swimming swim = new Swimming() {
@Override
public void swim() {
System.out.println("铁汁, 我们去游泳吧");
}
}
*/
swimming.swim();
}
}
七.Lambda表达式
7.1 lambda采用的是函数式编程思想
代码示例:
/*
游泳接口
*/
interface Swimming {
void swim();
}
public class TestSwimming {
public static void main(String[] args) {
// 通过匿名内部类实现
goSwimming(new Swimming() {
@Override
public void swim() {
System.out.println("铁汁, 我们去游泳吧");
}
});
/* 通过Lambda表达式实现
理解: 对于Lambda表达式, 对匿名内部类进行了优化
*/
goSwimming(() -> System.out.println("铁汁, 我们去游泳吧"));
}
/**
* 使用接口的方法
*/
public static void goSwimming(Swimming swimming) {
swimming.swim();
}
}
7.2 lambda表达式格式
- 格式: (形式参数) -> {代码块}
- 方法形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可
- ->:由英文中画线和大于符号组成,固定写法。代表指向动作
- 代码块:是我们具体要做的事情,也就是以前我们写的方法体内容
- 组成Lambda表达式的三要素:
- 形式参数,箭头,代码块
- lambda表达式使用前提:
有且仅有一个抽象方法的接口
7.3 Lambda表达式的省略模式
省略的规则
- 参数类型可以省略。但是有多个参数的情况下,不能只省略一个
- 如果参数有且仅有一个,那么小括号可以省略
如果代码块的语句只有一条,可以省略大括号和分号,和return关键字
public interface Addable {
int add(int x, int y);
}
public interface Flyable {
void fly(String s);
}
public class LambdaDemo {
public static void main(String[] args) {
// useAddable((int x,int y) -> {
// return x + y;
// });
//参数的类型可以省略
useAddable((x, y) -> {
return x + y;
});
// useFlyable((String 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);
}
}
7.4 Lambda表达式和匿名内部类的区别
- 所需类型不同
- 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
- Lambda表达式:只能是接口
- 使用限制不同
- 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
- 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
- 实现原理不同
- 匿名内部类:编译之后,产生一个单独的.class字节码文件
- Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成
八. 异常
异常的概述
异常就是程序出现了不正常的情况,程序执行过程中,出现非正常状况,最后导致JVM非正常停止
- 编译时异常
- 都是Exception类及其子类
- 必须显示处理,否则程序就会发生错误,无法通过编译
- 运行时异常
- 都是RuntimeException类及其子类
- 无需显示处理,也可以和编译时异常一样处理
如果程序出现了问题,我们没有做任何处理,最终JVM 会做默认的处理,处理方式有如下两个步骤:
- 把异常的名称,错误原因及异常出现的位置等信息输出在了控制台
- 程序停止执行
8.1 throws方式处理异常
注意事项
- 这个throws格式是跟在方法的括号后面的
- 编译时异常必须要进行处理,两种处理方案:try...catch …或者 throws,如果采用 throws 这种方案,
在方法上进行显示声明,将来谁调用这个方法谁处理,若调用中没有解决办法,则给虚拟机来处理
- 运行时异常因为在运行时才会发生,所以在方法后面可以不写,运行时出现异常默认交给jvm处理
8.2 throw抛出异常
- 格式throw new 异常();
- 注意这个格式是在方法内的,表示当前代码手动抛出一个异常,下面的代码不用再执行了
- throws和throw的区别
throws | throw |
用在方法声明后面,跟的是异常类名 | 用在方法体内,跟的是异常对象名 |
表示声明异常,调用该方法有可能会出现这样的异常 | 表示手动抛出异常对象,由方法体内的语句处理 |
示例代码
public class ExceptionDemo8 {
public static void main(String[] args) {
//int [] arr = {1,2,3,4,5};
int [] arr = null;
printArr(arr);//就会 接收到一个异常.
//我们还需要自己处理一下异常.
}
private static void printArr(int[] arr) {
if(arr == null){
//调用者知道成功打印了吗?
//System.out.println("参数不能为null");
throw new NullPointerException();
//当参数为null的时候,给调用者返回了一个错误,此时调用者就知道了他的错误,得写解决办法,若没有,则让虚拟机按虚拟机的办法来解决
//手动创建了一个异常对象,抛给了调用者,产生了一个异常
}else{
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
}
8.3 try-catch方式处理异常(应用)
会把try中所有的代码全部执行完毕,不会执行catch里面的代码
那么直接跳转到对应的catch语句中,try下面的代码就不会再执行了 当catch里面的语句全部执行完毕,表示整个体系全部执行完全,继续执行下面的代码
那么try...catch就相当于没有写.那么也就是自己没有处理. 默认交给虚拟机处理.
出现多个异常,那么就写多个catch就可以了. 注意点:如果多个异常之间存在子父类关系.那么父类一定要写在下面
8.4 Throwable成员方法
方法名 | 说明 |
public String getMessage() | 返回此 throwable 的详细消息字符串 |
public String toString() | 返回此可抛出的简短描述 |
public void printStackTrace() | 把异常的错误信息输出在控制台(字体是红色的) |
8.5 自定义异常
- 定义格式
try { 可能出现异常的代码; } catch(异常类名 变量名) { 异常的处理代码; }
- 执行流程
- 程序从 try 里面的代码开始执行
- 出现异常,就会跳转到对应的 catch 里面去执行
- 执行完毕之后,程序还可以继续往下执行
- 注意
- 如果 try 中没有遇到问题,怎么执行?
- 如果 try 中遇到了问题,那么 try 下面的代码还会执行吗?
- 如果出现的问题没有被捕获,那么程序如何运行?
- 同时有可能出现多个异常怎么处理?
实现步骤
- 定义异常类
- 写继承关系
- 提供空参构造
- 提供带参构造
异常类
public class AgeOutOfBoundsException extends RuntimeException {
public AgeOutOfBoundsException() {
}
public AgeOutOfBoundsException(String message) {
super(message);
}
}
九. Collection集合
Collection集合和数组的区别:
- 相同点:
都是容器,可以存储多个数据
- 不同点
- 数组的长度是不可变的,集合的长度是可变的
- 数组可以存基本数据类型和引用数据类型,集合只能存引用数据类型,如果要存基本数据类型,需要存对应的包装类
9.1 集合类体系结构
9.2 Collection 集合概述和使用
- Collection集合概述
- 是单例集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素
- JDK 不提供此接口的任何直接实现.它提供更具体的子接口(如Set和List)实现
- 创建Collection集合的对象
- 多态的方式
- 具体的实现类ArrayList
- Collection集合常用方法
方法名 | 说明 |
boolean add(E e) | 添加元素 |
boolean remove(Object o) | 从集合中移除指定的元素 |
boolean removeIf(Object o) | 根据条件进行移除 |
void clear() | 清空集合中的元素 |
boolean contains(Object o) | 判断集合中是否存在指定的元素 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中元素的个数 |
9.3 Collection集合的遍历
- 迭代器介绍
- 迭代器,集合的专用遍历方式
- Iterator iterator(): 返回此集合中元素的迭代器,通过集合对象的iterator()方法得到
- Iterator中的常用方法
boolean hasNext(): 判断当前位置是否有元素可以被取出 E next(): 获取当前位置的元素,将迭代器对象移向下一个索引位置
- 迭代器中删除的方法
void remove(): 删除迭代器对象当前指向的元素
- Collection集合的遍历
public class IteratorDemo1 {
public static void main(String[] args) {
//创建集合对象
Collection<String> c = new ArrayList<>();
//添加元素
c.add("hello");
c.add("world");
c.add("java");
c.add("javaee");
//Iterator<E> iterator():返回此集合中元素的迭代器,通过集合的iterator()方法得到
Iterator<String> it = c.iterator();
//用while循环改进元素的判断和获取
while (it.hasNext()) {
String s = it.next();
System.out.println(s);
}
}
}
9.4 增强for循环
介绍
- 它是JDK5之后出现的,其内部原理是一个Iterator迭代器
- 实现Iterable接口的类才可以使用迭代器和增强for
- 简化数组和Collection集合的遍历
public class MyCollectonDemo1 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("a"); list.add("b"); list.add("c"); list.add("d"); list.add("e"); list.add("f"); //1,数据类型一定是集合或者数组中元素的类型 //2,str仅仅是一个变量名而已,在循环的过程中,依次表示集合或者数组中的每一个元素 //3,list就是要遍历的集合或者数组 for(String str : list){ System.out.println(str); } } }
9.5 List集合
- List集合的概述
- 有序集合,这里的有序指的是存取顺序
- 用户可以精确控制列表中每个元素的插入位置,用户可以通过整数索引访问元素,并搜索列表中的元素
- 与Set集合不同,列表通常允许重复的元素
- List集合的特点
- 存取有序
- 可以重复
- 有索引
常用方法:
方法名 | 描述 |
void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
E get(int index) | 返回指定索引处的元素 |
public class MyListDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
//method1(list);
//method2(list);
//method3(list);
//method4(list);
}
private static void method4(List<String> list) {
// E get(int index) 返回指定索引处的元素
String s = list.get(0);
System.out.println(s);
}
private static void method3(List<String> list) {
// E set(int index,E element) 修改指定索引处的元素,返回被修改的元素
//被替换的那个元素,在集合中就不存在了.
String result = list.set(0, "qqq");
System.out.println(result);
System.out.println(list);
}
private static void method2(List<String> list) {
// E remove(int index) 删除指定索引处的元素,返回被删除的元素
//在List集合中有两个删除的方法
//第一个 删除指定的元素,返回值表示当前元素是否删除成功
//第二个 删除指定索引的元素,返回值表示实际删除的元素
String s = list.remove(0);
System.out.println(s);
System.out.println(list);
}
private static void method1(List<String> list) {
// void add(int index,E element) 在此集合中的指定位置插入指定的元素
//原来位置上的元素往后挪一个索引.
list.add(0,"qqq");
System.out.println(list);
}
}
9.6 数据结构
- 栈结构 先进后出
- 队列结构 先进先出
- 数组结构 查询快、增删慢
- 队列结构 查询慢、增删快
9.7 List集合的实现类
- ArrayList集合 底层是数组结构实现,查询快、增删慢
- LinkedList集合 底层是链表结构实现,查询慢、增删快
LinkedList集合的特有功能:
方法名 | 说明 |
public void addFirst(E e) | 在该列表开头插入指定的元素 |
public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst() | 返回此列表中的第一个元素 |
public E getLast() | 返回此列表中的最后一个元素 |
public E removeFirst() | 从此列表中删除并返回第一个元素 |
public E removeLast() | 从此列表中删除并返回最后一个元素 |