【黑马程序员】《Java基础入门》——02 面向对象

1 方法的初始化

1.1 类的基本概念

(1)属性:描述对象的特征(C++中的数据成员)
(2)方法:描述对象的行为(C++中的成员函数)
(3)类的内容:①成员变量 ②成员方法 ③成员内部类(Java特性)

1.2 成员变量的默认值

【黑马程序员】《Java基础入门》——02 面向对象_第1张图片
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类常用方法

【黑马程序员】《Java基础入门》——02 面向对象_第2张图片

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表达式后可以优化一下

题外话:引用方法有两种基本方式,通过类名对象名。引用构造器也有两种基本方式,通过 thissuper 关键字。讲真,这些基本方式已经够用了,还有没有必要掌握其他方式?或者像文化人说的,有没有必要知道茴香豆的“茴”有多少种写法?完全没必要嘛。嗯……但是,做开发是要看别人写的代码的,每个人的习惯可能有所不同,所以为了能看懂别人写的代码,还是有必要了解一下其他方法的。

  • Lambda表达式支持的引用类型
    【黑马程序员】《Java基础入门》——02 面向对象_第3张图片

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类

【黑马程序员】《Java基础入门》——02 面向对象_第4张图片

9.1.2 Throwable类常用方法

【黑马程序员】《Java基础入门》——02 面向对象_第5张图片

9.1.3 异常的类型

(1)编译时异常,这种异常必须要进行处理
(2)运行时异常,这种异常即使不编写异常处理代码,依然可以通过编译,但在运行时可能出现异常。
【黑马程序员】《Java基础入门》——02 面向对象_第6张图片

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:指定输出文档需包含作者信息
【黑马程序员】《Java基础入门》——02 面向对象_第7张图片
在当前目录下打开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常用包
【黑马程序员】《Java基础入门》——02 面向对象_第8张图片

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

你可能感兴趣的:(#,01,Java学习笔记,java,开发语言)