黑马程序员java基础入门(二)面向对象
1 方法的初始化
1.1 类的基本概念
(1)属性:描述对象的特征(C++中的数据成员)
(2)方法:描述对象的行为(C++中的成员函数)
(3)类的内容:①成员变量 ②成员方法 ③成员内部类(Java特性)
1.2 成员变量的默认值
PS:Java的成员变量可以在类中进行初始化(C++不行)
1.3 构造方法及其重载
class Person{
private String name;
private int age;
// 有两个参数的构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 有一个参数的构造方法
public Person(String name) {
this.name = name;
this.age = 18; // 默认18岁
}
public void speak() {
System.out.println("大家好,我叫" + name + ",今年" + age + "岁啦");
}
}
public class Demo {
public static void main(String[] args) {
Person p1 = new Person("李知因", 23);
Person p2 = new Person("李知恩");
p1.speak();
p2.speak();
}
}
PS:
(1)与C++类似,Java中的每个类都至少有一个构造方法,如果没有显式定义,系统将创建方法体为空的默认构造方法
(2)构造方法一般用public来修饰(单例模式使用private的构造方法)
(3)构造方法名与类名相同,无返回值,参数可有可无
2 this关键字
this用于在方法中访问对象的其他成员,可理解为当前对象的引用
2.1 解决成员变量与局部变量的同名冲突问题
class Person{
int age; //成员变量
public Person(int age){ //局部变量
this.age = age; //局部变量赋值给成员变量
}
}
2.2 在构造方法中调用其他构造方法
class Person {
public Person(){
System.out.println("无参的构造方法被调用了……");
}
public Person(String name) {
this(); //调用无参的构造方法
System.out.println("有参的构造方法被调用了……");
}
}
public class Demo {
public static void main(String[] args) {
Person p = new Person("itcast"); //实例化对象
}
}
/*
无参的构造方法被调用了……
有参的构造方法被调用了……
*/
PS:
(1)构造方法是在实例化对象时由JVM自动调用的,在程序中不能像调用其他方法一样调用构造方法
(2)只能在构造方法中使用this调用其他构造方法,不能在成员方法中使用
(3)在构造方法中,使用this的方法调用语句必须放在第一行,且只能出现一次
3 static关键字
static用于修饰类的成员,如成员变量、成员方法、代码块
3.1 静态变量的定义与使用
class Student {
static String schoolName; // 定义静态变量schoolName
}
public class Demo {
public static void main(String[] args) {
Student stu1 = new Student();
Student stu2 = new Student();
Student.schoolName = "传智播客"; // 为静态变量赋值
// stu1.schoolName = "传智播客"; // 为静态变量赋值
System.out.println("我的学校是" + stu1.schoolName); // 打印第一个学生对象的学校
System.out.println("我的学校是" + stu2.schoolName); // 打印第二个学生对象的学校
}
}
/*
我的学校是传智播客
我的学校是传智播客
*/
PS:
①静态变量是用static修饰的成员变量,从属于类,为所有对象共享
②static只能修饰成员变量,不能修饰局部变量
3.2 静态方法的定义与使用
class Person {
public static void sayHello() { // 定义静态方法
System.out.println("hello");
}
}
public class Demo {
public static void main(String[] args) {
Person.sayHello(); // 调用静态方法
}
}
/*
hello
*/
PS:
(1)静态成员属于类,(在没有创建对象时)可通过“类名.成员名”来访问
(2)创建对象后,也可以通过“对象名.成员名”来调用静态成员
(3)静态方法只能调用静态成员
3.3 静态代码块的定义与使用
定义:静态代码块是用static修饰的代码块(用{ }括起来)
特点:静态代码块在创建对象加载类时执行,且只执行一次
作用:通常用于初始化成员变量
class Person {
static String country;
// 下面是一个静态代码块
static {
country = "china";
System.out.println("Person类中的静态代码块执行了");
}
}
public class Demo {
// 静态代码块
static {
System.out.println("测试类的静态代码块执行了");
}
public static void main(String[] args) {
// 下面的代码创建了三个Person对象,但是静态代码只会执行一次
Person p1 = new Person();
Person p2 = new Person();
Person p3 = new Person();
}
}
/*
测试类的静态代码块执行了
Person类中的静态代码块执行了
*/
PS:
(1)静态代码块只能调用静态成员
(2)与静态变量和静态方法不同,执行静态代码块需要创建对象,因为静态代码块是在加载类时执行的
3.4 单例模式
单例模式是Java的一种设计模式,可以保证类在程序运行期间只有一个对象。
class Single {
// 1、私有化构造方法
private Single() {}
// 2、类中创建一个私有静态对象
private static Single INSTANCE = new Single();
// 3、提供返回该对象的静态方法
public static Single getInstance() {
return INSTANCE;
}
}
public class Demo {
public static void main(String[] args) {
Single s1 = Single.getInstance();
Single s2 = Single.getInstance();
System.out.println(s1.equals(s2));
}
}
/*
true
*/
PS:两次调用getInstance()得到的是同一个对象,说明Single是一个单例的类
单例模式的另一种形式↓
class Single {
// 1、私有化构造方法
private Single() {}
// 2、类中创建一个对象
public static final Single INSTANCE = new Single();
}
public class Demo {
public static void main(String[] args) {
Single s1 = Single.INSTANCE;
Single s2 = Single.INSTANCE;
System.out.println(s1.equals(s2));
}
}
/*
true
*/
4 类的继承
4.1 继承与重写
4.1.1 类的继承
class A extends B
PS:
(1)Java继承时采用extends关键字(C++声明继承方式)
(2)Java中的类只支持单继承,不支持多继承(与C++不同)
(3)多个类可以继承同一个父类
4.1.2 重写父类方法
(1)只能重写方法体
(2)重写父类方法时,子类不能使用比父类中被重写的方法更严格的访问权限。
4.2 super关键字
super关键字用于访问父类的成员
4.2.1 调用父类的成员变量
super.父类的成员变量
4.2.2 调用父类的成员方法
super.父类的成员方法
4.2.3 调用父类的构造方法
super(有参或无参);
PS:
(1)super调用父类构造方法的语句必须放在子类构造方法的第一行,且只能出现一次
(2)super VS this:
1)this可调用本类的成员变量和成员方法,可在构造方法中调用另一构造方法(横向调用)
2)super可调用父类的成员变量和成员方法,可在子类构造方法中调用父类构造方法(纵向调用)
4.3 Object类
4.3.1 简介
Object类是所有类的父类,通常被称为超类、基类或根类。当定义一个类时,如果没有使用extends关键字为这个类显式地指定父类,那么该类会默认继承 Object 类。
4.3.2 Object类常用方法
4.4 final关键字
final关键字可用于修饰类、变量和方法,它有"不可更改"或者"最终"的含义
(1)final修饰的类不能被继承
(2)final 修饰的方法不能被子类重写
(3)final修饰的变量(成员变量和局部变量)是常量,只能赋值一次
5 抽象类与接口
5.1 抽象类
抽象类与抽象方法用abstract关键字进行修饰。
//定义抽象类Animal
abstract class Animal {
// 定义抽象方法shout()
public abstract void shout();
}
//定义Dog类继承抽象类Animal
class Dog extends Animal {
// 实现抽象方法shout(),编写方法体
public void shout() {
System.out.println("汪汪……");
}
}
//定义测试类
public class Demo {
public static void main(String[] args) {
Dog dog = new Dog(); // 创建Dog类的实例对象
dog.shout(); // 调用dog对象的shout()方法
}
}
/*
汪汪……
*/
PS:
(1)包含抽象方法的类是抽象类(C++包含纯虚函数的类是抽象类)
(2)抽象类不能被实例化,抽象方法没有方法体,不能被调用
5.2 接口
5.2.1 JDK8的接口
接口是一种特殊的抽象类。在JDK8中,除了抽象方法,接口中还可以有默认方法和静态方法,且这两种方法都能有方法体。
5.2.2 接口的定义与实现
//定义了Animal接口
interface Animal {
int ID = 1; // 定义全局常量
void breathe(); // 定义抽象方法
// 定义一个默认方法
default void getType(String type){
System.out.println("该动物属于:"+type);
}
// 定义一个静态方法
public static int getID(){
return Animal.ID;
}
}
//Dog类实现了Animal接口
class Dog implements Animal {
// 实现breathe()方法
public void breathe() {
System.out.println("狗在呼吸");
}
}
//定义测试类
public class Demo {
public static void main(String args[]) {
System.out.println(Animal.getID()); // 调用静态方法
Dog dog = new Dog(); // 创建Dog类的对象
System.out.println(dog.ID); // 获取接口全局常量
dog.breathe(); // 调用已实现的抽象方法
dog.getType("犬科"); // 调用接口默认方法
}
}
/*
1
1
狗在呼吸
该动物属于:犬科
*/
PS:
(1)接口中的方法默认为抽象方法,用“public abstract”修饰
(2)接口中的变量默认为全局常量,用“public static final”修饰
(3)接口无法被实例化,需要定义一个类,并实现(implements)接口的所有抽象方法(若该类为抽象类,则只需实现部分方法)
(4)一个接口可继承多个接口,用extends来声明继承关系
(5)一个类只能继承一个类(单继承),但可以实现多个接口,还可以在继承父类的同时实现接口,用implements来声明实现关系(此时extends需在implements之前)
6 多态性
6.1 多态
Java通过在方法中传入不同类型的对象实现多态,形参是父类类型(C++通过虚函数实现多态)
//定义接口Anmal
interface Animal {
void shout(); // 定义抽象shout()方法
}
//定义Cat类实现Animal接口
class Cat implements Animal {
//实现shout()方法
public void shout() {
System.out.println("喵喵……");
}
}
//定义Dog类实现Animal接口
class Dog implements Animal {
// 实现shout()方法
public void shout() {
System.out.println("汪汪……");
}
}
//定义测试类
public class Demo {
public static void main(String[] args) {
Animal an1 = new Cat(); // 创建Cat对象,使用Animal类型的变量an1引用
Animal an2 = new Dog(); // 创建Dog对象,使用Animal类型的变量an2引用
animalShout(an1); // 调用animalShout()方法,将an1作为参数传入
animalShout(an2); // 调用animalShout()方法,将an2作为参数传入
}
// 定义静态的animalShout()方法,接收一个Animal类型的参数
public static void animalShout(Animal an) {
an.shout(); // 调用实际参数的shout()方法
}
}
/*
喵喵……
汪汪……
*/
6.2 对象的类型转换
将子类对象赋值给父类对象实质上是C++的赋值兼容问题,此时父类对象只能调用子类从父类继承的方法,而不能调用子类新增的方法
PS:用instanceof可以判断一个对象是否为某个类(或接口)的实例,将父类对象强转为子类类型,从而调用原本无法调用的子类方法
//定义接口Anmal
interface Animal {
void shout(); // 定义抽象shout()方法
}
//定义Cat类实现Animal接口
class Cat implements Animal {
// 实现shout()方法
public void shout() {
System.out.println("喵喵……");
}
}
//定义Dog类实现Animal接口
class Dog implements Animal {
// 实现shout()方法
public void shout() {
System.out.println("汪汪……");
}
}
//定义测试类
public class Demo {
public static void main(String[] args) {
Animal an1 = new Cat(); // 创建Cat对象,使用Animal类型的变量an1引用
Animal an2 = new Dog(); // 创建Dog对象,使用Animal类型的变量an2引用
animalShout(an1); // 调用animalShout()方法,将an1作为参数传入
animalShout(an2); // 调用animalShout()方法,将an2作为参数传入
}
// 定义静态的animalShout()方法,接收一个Animal类型的参数
public static void animalShout(Animal animal) {
if(animal instanceof Cat) { // 判断animal是否为Cat类的实例对象
Cat cat = (Cat)animal; // 将animal强转为Cat类型
cat.shout();
}else if(animal instanceof Dog) {
Dog dog = (Dog)animal;
dog.shout();
}
}
}
/*
喵喵……
汪汪……
*/
7 内部类
在Java中,允许在类的内部定义类,即内部类,这个内部类所在的类称为外部类。
7.1 内部类的种类
7.1.1 静态内部类
//定义外部类Outer
class Outer {
static int m = 0; // 定义外部类静态变量m
static class Inner {
void show() {
// 静态内部类访问外部类静态成员
System.out.println("外部类静态变量m="+m);
}
}
}
//定义测试类
public class Demo {
public static void main(String[] args) {
// 静态内部类可以直接通过外部类创建
Outer.Inner inner = new Outer.Inner();
inner.show();
}
}
/*
外部类静态变量m=0
*/
PS:
(1)在主方法中创建静态内部类对象:外部类名.内部类名 变量名 = new 外部类名.内部类名();
(2)在静态内部类中只能访问外部类的静态成员
(3)非静态内部类中不允许定义静态成员
7.1.2 成员内部类
外部类与内部类互相访问彼此的成员
//定义外部类Outer
class Outer {
int m = 0; // 定义外部类的成员变量
// 定义外部类成员方法
void test1() {
System.out.println("外部类成员方法");
}
// 定义成员内部类Inner
class Inner {
int n = 1;
// 1、定义内部类方法,访问外部类成员变量和方法
void show1() {
System.out.println("外部类成员变量m="+m);
test1();
}
void show2(){
System.out.println("内部类成员方法");
}
}
// 2、定义外部类方法,访问内部类变量和方法
void test2() {
Inner inner = new Inner();
System.out.println("内部类成员变量n="+inner.n);
inner.show2();
}
}
//定义测试类
public class Demo {
public static void main(String[] args) {
Outer outer = new Outer(); // 创建外部类对象
Outer.Inner inner = outer.new Inner(); // 创建内部类对象
inner.show1(); // 测试在成员内部类中访问外部类成员变量和方法
outer.test2(); // 测试在外部类中访问内部类成员变量和方法
}
}
/*
外部类成员变量m=0
外部类成员方法
内部类成员变量n=1
内部类成员方法
*/
7.1.3 方法内部类
外部类与内部类互相访问彼此的成员
//定义外部类Outer
class Outer {
int m = 0;
void test1(){
System.out.println("外部类成员方法");
}
void test2() {
// 1、定义方法内部类Inner,在方法内部类中访问外部类变量和方法
class Inner {
int n = 1;
void show() {
System.out.println("外部类变量m="+m);
test1();
}
}
// 2、在创建方法内部类的方法中,调用方法内部类变量和方法
Inner inner = new Inner();
System.out.println("方法内部类变量n="+inner.n);
inner.show();
}
}
//定义测试类
public class Demo {
public static void main(String[] args) {
Outer outer= new Outer();
outer.test2(); // 通过外部类对象调用创建了方法内部类的方法
}
}
/*
方法内部类变量n=1
外部类变量m=0
外部类成员方法
*/
7.1.4 匿名内部类(见7.2.4)
7.2 内部类的作用
7.2.1 无限制地访问外部类的成员变量和方法
//定义外部类Outer
class Outer {
private int m = 0; // 定义外部类的成员变量
// 定义外部类成员方法
void test() {
System.out.println("外部类成员方法");
}
// 定义成员内部类Inner
class Inner {
// 定义内部类方法,访问外部类(私有)成员变量和方法
void show() {
System.out.println("外部类成员变量m="+m);
test();
}
}
}
//定义测试类
public class Demo {
public static void main(String[] args) {
Outer.Inner inner = new Outer().new Inner(); // 创建内部类对象
inner.show(); // 测试在成员内部类中访问外部类成员变量和方法
}
}
/*
外部类成员变量m=0
外部类成员方法
*/
7.2.2 实现信息隐藏
外部类不能使用 private 和 protected 来修饰,而内部类可以使用 private 和 protected 来修饰。当使用 private 来修饰内部类的时候,内部类就对外隐藏了。
interface Animal{
void shout();
}
class LandAnimal{
private class Cat implements Animal{
public void shout() {
System.out.println("喵星人:你不知道我是如何发出喵喵声的");
}
}
// 得到实例化的接口对象
public Animal getCat() {
return new Cat();
}
}
//定义测试类
public class Demo {
public static void main(String[] args) {
LandAnimal landAnimal = new LandAnimal();
Animal animal = landAnimal.getCat();
animal.shout();
}
}
/*
喵星人:你不知道我是如何发出喵喵声的
*/
PS:在主函数中,我们只能知道LandAnimal类的getCat()方法可以返回一个Animal类型的接口实例,但不知道这个实例是如何实现的,也不知道实现这一接口的类的名字,所以对外实现了信息隐藏。
7.2.3 间接实现多重继承
class A{
public String name() {
return "A";
}
}
class B{
public String type() {
return "class";
}
}
class C{
// 内部类1继承A
private class InnerOne extends A{
public String name() {
return super.name();
}
}
// 内部类2继承B
private class InnerTwo extends B{
public String type() {
return super.type();
}
}
public void speak() {
// 创建内部类对象,让其访问自己的成员方法
InnerOne innerOne = new InnerOne();
InnerTwo innerTwo = new InnerTwo();
System.out.println("my name is "+ innerOne.name() + ", my type is " + innerTwo.type());
}
}
//定义测试类
public class Demo {
public static void main(String[] args) {
C c = new C();
c.speak();
}
}
/*
my name is A, my type is class
*/
类C中有两个内部类 InnerOne 和 InnerTwo ,分别继承了类A和类B。由于类C可以访问内部类 InnerOne 和 InnerTwo 的成员,那么相当于类C间接地继承了类A和类B,打破了Java只支持单继承的限制。
7.2.4 匿名内部类可以简化接口的实现
interface Animal {
void shout();
}
public class Demo {
public static void main(String[] args) {
String name = "小花";
// 定义匿名内部类作为参数传递给animalShout()方法
animalShout(new Animal() {
// 实现shout()方法
public void shout() {
// JDK 8开始,方法内部类、匿名内部类可以访问非final的局部变量
System.out.println(name + "喵喵...");
}
});
}
// 定义静态方法animalShout(),接收接口类型参数
public static void animalShout(Animal an) {
an.shout(); // 调用传入对象an的shout()方法
}
}
/*
小花喵喵...
*/
8 Lambda表达式
8.1 Lambda表达式入门
8.1.1 简介
Lambda表达式可以简化接口的实现,也可以简化对集合以及数组数据的遍历、过滤和提取等操作。
一个Lambda表达式由三个部分组成,分别为参数列表、"->"和表达式主体,可以形象地理解为将一堆数据扔给方法体,其语法格式如下∶
([数据类型 参数名,数据类型 参数名,…])->{表达式主体}
(1)参数列表:用于向表达式主体(接口的抽象方法)传递所需的参数。可以省略参数的数据类型,同时,如果只有一个参数,则可以省略括号()。
(2)->∶用来指定参数数据指向
(3)表达式主体∶即抽象方法体,如果表达式主体只有一条语句,那么可以省略包含主体的大括号。另外,在Lambda表达式主体中允许有返回值,当只有一条 return语句时,也可以省略 return关键字。
PS:适于实现仅有一个抽象方法的接口,作为方法的参数进行传递
8.1.2 用Lambda表达式实现接口
//定义动物类接口
interface Animal {
void shout();
}
public class Demo {
public static void main(String[] args) {
String name = "小花";
// 1、匿名内部类作为参数传递给animalShout()方法(用匿名内部类实现接口)
animalShout(new Animal() {
public void shout() {
System.out.println("匿名内部类输出:" + name + "喵喵...");
}
});
// 2、使用Lambda表达式作为参数传递给animalShout()方法(用Lambda表达式实现接口)
animalShout(() -> System.out.println("Lambda表达式输出:" + name + "喵喵..."));
}
// 创建一个animalShout()静态方法,接收接口类型的参数
public static void animalShout(Animal an) {
an.shout();
}
}
/*
匿名内部类输出:小花喵喵...
Lambda表达式输出:小花喵喵...
*/
PS:至此,我们学到了接口的3种实现方式:普通方式、匿名内部类、Lambda表达式。对比发现,当接口中只有一个抽象方法时,用Lambda表达式比用匿名内部类更为简洁。
8.2 函数式接口
函数式接口是特殊的接口,有且仅有一个抽象方法。Lambda 表达式就是Java中函数式编程的体现。
//定义无参、无返回值的函数式接口
@FunctionalInterface //标注函数式接口
interface Animal {
void shout();
}
//定义有参、有返回值的函数式接口
interface Calculate {
int sum(int a, int b);
}
public class Demo {
public static void main(String[] args) {
// 分别对两个函数式接口进行测试
animalShout(() -> System.out.println("无参、无返回值的函数式接口调用"));
showSum(10, 20, (x, y) -> x + y);
}
// 创建一个动物叫的方法,并传入接口对象Animal作为参数
private static void animalShout(Animal animal) {
animal.shout();
}
// 创建一个求和的方法,并传入两个int类型以及接口Calculate类型的参数
private static void showSum(int x, int y, Calculate calculate) {
System.out.println(x + "+" + y + "的和为:" + calculate.sum(x, y));
}
}
/*
无参、无返回值的函数式接口调用
10+20的和为:30
*/
8.3 方法引用与构造器引用
Lambda表达式的主体只有一条语句时,程序不仅可以省略包含主体的大括号,还可以通过英文双冒号"::"的语法格式来引用方法和构造器,这两种形式可以进一步简化 Lambda表达式的书写,其本质都是对 Lambda表达式的主体部分已存在的方法进行直接引用,主要区别就是对普通方法与构造方法的引用而已。(说白了作用就是简化Lambda表达式的书写,写好了Lambda表达式后可以优化一下)
题外话:引用方法有两种基本方式,通过类名或对象名。引用构造器也有两种基本方式,通过 this 或 super 关键字。讲真,这些基本方式已经够用了,还有没有必要掌握其他方式?或者像文化人说的,有没有必要知道茴香豆的“茴”有多少种写法?完全没必要嘛。嗯……但是,做开发是要看别人写的代码的,每个人的习惯可能有所不同,所以为了能看懂别人写的代码,还是有必要了解一下其他方法的。
- Lambda表达式支持的引用类型
8.3.1 类名引用普通方法
//定义一个函数式接口
@FunctionalInterface
interface Printable{
void print(StringUtils su, String str);
}
class StringUtils {
public void printUpperCase(String str) {
System.out.println(str.toUpperCase());
}
}
//定义测试类
public class Demo {
private static void printUpper(StringUtils su, String text, Printable pt) {
pt.print(su, text);
}
public static void main(String[] args) {
// 使用Lambda表达式
printUpper(new StringUtils(), "Hello", (object, str) -> object.printUpperCase(str));
// 使用类名引用普通方法
printUpper(new StringUtils(), "Hello", StringUtils::printUpperCase);
}
}
/*
HELLO
HELLO
*/
8.3.2 类名引用静态方法
//定义一个函数式接口
@FunctionalInterface
interface Calcable {
int calc(int num);
}
//定义一个类,并在类中定义一个静态方法
class Math {
// 定义一个求绝对值方法
public static int abs(int num) {
if (num < 0) {
return -num;
} else {
return num;
}
}
}
//定义测试类
public class Demo {
private static void printAbs(int num, Calcable calcable) {
System.out.println(calcable.calc(num));
}
public static void main(String[] args) {
// 1、使用Lambda表达式
printAbs(-10, n -> Math.abs(n));
// 2、使用类名引用静态方法
printAbs(-10, Math::abs);
}
}
/*
10
10
*/
总结:类的普通方法和静态方法都可以通过“类名::方法名”的方式进行调用。
8.3.3 对象名引用方法
对象名::方法名
//定义一个函数式接口
@FunctionalInterface
interface Printable{
void print(String str);
}
class StringUtils {
public void printUpperCase(String str) {
System.out.println(str.toUpperCase());
}
}
//定义测试类
public class Demo {
private static void printUpper(String text, Printable pt) {
pt.print(text);
}
public static void main(String[] args) {
StringUtils stu = new StringUtils();
// 使用Lambda表达式
printUpper("Hello", str -> stu.printUpperCase(str));
// 使用对象名调用方法
printUpper("Hello", stu::printUpperCase);
}
}
/*
HELLO
HELLO
*/
8.3.4 构造器引用
类名::new
//定义一个函数式接口
@FunctionalInterface
interface PersonBuilder {
Person buildPerson(String name);
}
//定义一个Person类,并添加有参构造方法
class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
//定义测试类
public class Demo {
public static void printName(String name, PersonBuilder builder) {
System.out.println(builder.buildPerson(name).getName());
}
public static void main(String[] args) {
// 使用Lambda表达式方式
printName("李知恩", name -> new Person(name));
// 使用构造器引用的方式
printName("李知恩", Person::new);
}
}
/*
李知恩
李知恩
*/
9 异常
Java自带异常类,通过异常处理机制对程序运行时出现的各种问题进行处理。
9.1 异常概要
9.1.1 Throwable类
9.1.2 Throwable类常用方法
9.1.3 异常的类型
(1)编译时异常,这种异常必须要进行处理
(2)运行时异常,这种异常即使不编写异常处理代码,依然可以通过编译,但在运行时可能出现异常。
9.2 捕获并处理异常
try…catch和finally
public class Demo {
// 下面的方法实现了两个整数相除
public static int divide(int x, int y) {
try {
int result = x / y; // 定义一个变量result记录两个数相除的结果
return result; // 将结果返回
} catch (Exception e) { // 对异常进行捕获处理
System.out.println("捕获的异常信息为:" + e.getMessage());
e.printStackTrace();
} finally {
System.out.println("执行finally代码块,无论程序是否异常,都会执行");
}
// 定义当程序发生异常直接返回-1
return -1;
}
public static void main(String[] args) {
int result = divide(4, 0); // 调用divide()方法
if(result == -1){ // 对调用方法返回结果进行判断
System.out.println("程序发生异常!");
}else{
System.out.println(result);
}
}
}
/*
捕获的异常信息为:/ by zero
java.lang.ArithmeticException: / by zero
at demo/demo.Demo.divide(Demo.java:7)
at demo/demo.Demo.main(Demo.java:19)
执行finally代码块,无论程序是否异常,都会执行
程序发生异常!
*/
PS:无论程序是发生异常还是用return语句结束,finally代码块都会执行(除非在此之前调用了System.exit),所以在实际开发中经常在finally中完成必须做的事,如释放系统资源、关闭线程池等。
9.3 throws关键字
throws关键字用于声明方法可能抛出的异常,并将出现的异常从当前方法向上抛出,转交其调用者进行异常处理(其调用者也可以继续将该异常抛出)。
public class Demo {
// 下面的方法实现了两个整数相除,并使用throws关键字声明抛出异常
public static int divide(int x, int y) throws Exception {
int result = x / y; //定义一个变量result记录两个数相除的结果
return result; //将结果返回
}
public static void main(String[] args) {
try {
int result = divide(4, 0); //调用divide()方法
System.out.println(result);
} catch (Exception e) { //对捕获到的异常进行处理
System.out.println("捕获的异常信息为:" + e.getMessage());
}
}
}
9.4 throw关键字
与 throws有所不同的是,throw用于方法体内,并且抛出的是一个异常类对象,而 throws关键字用在方法声明中,用来指明方法可能抛出的多个异常。
- 在方法中抛出异常对象
public class Demo {
// 定义printAge()输出年龄
public static void printAge(int age) throws Exception {
if(age <= 0){
// 对业务逻辑进行判断,当输入年龄为负数时抛出异常
throw new Exception("输入的年龄有误,必须是正整数!");
}else {
System.out.println("此人年龄为:"+age);
}
}
public static void main(String[] args) {
// 下面的代码定义了一个try…catch语句用于捕获异常
int age = -1;
try {
printAge(age);
} catch (Exception e) { // 对捕获到的异常进行处理
System.out.println("捕获的异常信息为:" + e.getMessage());
}
}
}
/*
捕获的异常信息为:输入的年龄有误,必须是正整数!
*/
9.5 自定义异常
Java允许自定义异常,但自定义的异常必须继承Exception类或其子类。在实际开发中,如果没有特殊的要求,自定义的异常类只需继承 Exception类,在构造方法中使用super()语句调用 Exception 的构造方法即可。
//下面的代码是自定义一个异常类继承自Exception
class DivideByMinusException extends Exception{
public DivideByMinusException (){
super(); // 调用Exception无参的构造方法
}
public DivideByMinusException (String message){
super(message); // 调用Exception有参的构造方法
}
}
public class Demo {
// 下面的方法实现了两个整数相除,
public static int divide(int x,int y) throws DivideByMinusException {
if (x < 0) {
// 使用throw关键字声明异常对象
throw new DivideByMinusException("被除数是负数!");
}
int result = x / y; // 定义一个变量result记录两个数相除的结果
return result; // 将结果返回
}
public static void main(String[] args) {
try {
int result = divide(-2, 3);
System.out.println(result);
} catch (DivideByMinusException e) {
System.out.println("捕获的异常信息为:" + e.getMessage());
}
}
}
/*
捕获的异常信息为:被除数是负数!
*/
PS:throw用于在方法体中抛出一个异常类对象。通过 throw关键字抛出异常后,调用者还需要使用throws关键字或try…catch对异常进行处理。如果 throw抛出的是Error、RuntimeException或它们的子类异常对象,则无须使用 throws关键字或 try…catch 对异常进行处理。
10 其他
10.1 垃圾回收
Java中引入了垃圾回收机制,JVM会在垃圾堆积到一定程度时自动回收垃圾对象所占的内存,因此不必过于担心垃圾对象的回收问题。
- 通知JVM立即进行垃圾回收
class Person {
// 下面定义的finalize方法会在垃圾回收前被调用
public void finalize() { //重写finalize方法
System.out.println("对象将被作为垃圾回收...");
}
}
public class Demo {
public static void main(String[] args) {
// 下面是创建了两个Person对象
Person p1 = new Person ();
Person p2 = new Person ();
// 下面将变量置为null,让对象成为垃圾
p1 = null;
p2 = null;
// 调用方法进行垃圾回收
System.gc();
for (int i = 0; i < 1000000000; i++) {
// 为了延长程序运行的时间
}
}
}
/*
对象将被作为垃圾回收...
对象将被作为垃圾回收...
*/
PS:finalize()会在释放对象时自动调用,必须用public进行修饰,且返回值类型为void
10.2 Java帮助文档
Java文档注释格式:/**……*/,可在命令行中使用javadoc命令生成HTML格式的帮助文档
/**
* Title: Person类
* Description: 通过Person类来说明Java中的文档注释
* Company: Outcast
* @author Outcast
* @version 1.0
*/
public class Person {
public String name;
/**
* 这是Person类的构造方法
* @param name Person的名字
*/
public Person(String name){
// 执行语句;
}
/**
* 这是read()方法的说明
* @param bookName 读的书的名字
* @param time 读书所需的时间
* @return 读的书的数量
*/
public int read(String bookName,int time){
return time;
}
}
用命令行生成帮助文档
javadoc -d . -version -author Person.java
PS:
①-d:指定输出文档存放的目录
② . :表示当前目录
③-version:指定输出文档需包含版本信息
④-author:指定输出文档需包含作者信息
在当前目录下打开HTML文件即可查看文档信息
10.3 包
10.3.1 定义并使用包
Java中的包用于存放类,通常功能相同的类存放在相同的包中
- 包的声明
package demo; //demo为包名
PS:包的声明必须位于源文件第一行
- 编译声明了包的源文件
javac -d . Hello.java //生成带包目录的.class文件并存放在当前目录下
PS:
(1)-d用于指定生成的类文件存放的位置
(2). 表示当前目录
(3)也可以把 .class文件放在其他目录下,如
javac -d E:\wordspace Hello.java //生成带包目录的.class文件并存放在E:\wordspace文件夹中
- 运行声明了包的源文件
java demo.Hello
10.3.2 导包
(1)导入类的语法:import 包名.类名
(2)当需要使用的类较多时,可用“import 包名.*”来导入该包下所有的类
(3)Java常用包
10.3.3 打包Java程序
jar命令可以将类文件打包成“.jar”文件(也称jar包),使用jar包时,只需在classpath环境变量中包含此jar文件的路径
- 压缩jar文件
jar -cvf Hello.jar demo //把demo目录下的全部内容生成一个Hello.jar文件
PS:
(1)-c用于创建归档的文件
(2)-v代表在标准输出中生成详细输出
(3)-f用于指定生成的文件名
- 打开MANIFEST.MF文件,指定jar包的主类(先将jar包解压,再以记事本方式打开MANIFEST.MF文件)
Main-Class: demo.Hello //指定jar包的主类为demo.Hello
- 运行jar文件
java -jar Hello.jar