课程链接:https://edu.aliyun.com/course/1011
(还是建议去看课程,笔记仅供参考。
由于文中的所有内容均为手敲,并且有些代码并未验证,因此如有错误,烦请指出~ 谢谢~~~
并且请注意:文中的笔记并不完全与视频一致,包括代码与文字描述。)
(思维导图在最后)
————————————————————————————————————
class Person { // 定义一个类
String name; // 人的姓名
int age; // 人的年龄
public void tell() {
System.out.println("姓名:" + name + ",年龄:" + age);
}
}
public class JavaDemo {
public static void main(String[] args) {
Person per = new Person(); // 声明并实例化对象
per.name = "张三";
per.age = 18;
per.tell(); // 进行方法的调用
}
}
public class JavaDemo {
public static void main(String[] args) {
Person per = null; // 声明对象
per = new Person(); // 实例化对象
per.name = "张三";
per.age = 18;
per.tell(); // 进行方法的调用
}
}
public class JavaDemo {
public static void main(String[] args) {
Person per = null; // 声明对象
per.name = "张三";
per.age = 18;
per.tell(); // 进行方法的调用
}
}
上述代码会产生“java.lang.NullPointerException”的空指针异常,因为此时并没有在堆内存中开辟空间,这种情况只发生于引用数据类型。
public class JavaDemo {
public static void main(String[] args) {
Person per1 = new Person(); // 声明并实例化对象
per1.name = "张三";
per1.age = 18;
Person per2 = per1; // 引用传递
per2.age = 80;
per1.tell(); // 进行方法的调用
}
}
public class JavaDemo {
public static void main(String[] args) {
Person per = new Person(); // 声明并实例化对象
per.name = "张三";
per.age = 18;
change(per);
per.tell(); // 进行方法的调用
}
public static void change(Person temp) {
temp.age = 80;
}
}
引用传递的本质:一场调戏堆内存的游戏。
引用传递会产生垃圾。
public class JavaDemo {
public static void main(String[] args) {
Person per1 = new Person(); // 声明并实例化对象
Person per2 = new Person();
per1.name = "张三";
per1.age = 18;
per2.name = "李四";
per2.age = 19;
per2 = per1; // 引用传递
per2.age = 80;
per1.tell(); // 进行方法的调用
}
}
class Person { // 定义一个类
private String name; // 人的姓名
private int age; // 人的年龄
public void tell() {
System.out.println("姓名:" + name + ",年龄:" + age);
}
}
属性一旦封装后,外部将不可以直接进行访问。
【setter、getter】设置或取得属性使用setXxx()、getXxx()方法。以“private String name”为例,
class Person { // 定义一个类
private String name; // 人的姓名
private int age; // 人的年龄
public void tell() {
System.out.println("姓名:" + name + ",年龄:" + age);
}
public String getName() {
return name;
}
public void setName(String n) {
name = n;
}
public int getAge() {
return age;
}
public void setAge(int a) {
age = a;
}
}
public class JavaDemo {
public static void main(String[] args) {
Person per = new Person(); // 声明并实例化对象
per.setName("张三"); // 在类外部通过方法修改私有属性
per.setAge(18);
per.tell(); // 进行方法的调用
}
}
class Person { // 定义一个类
private String name; // 人的姓名
private int age; // 人的年龄
public Person(String n, int a) { // 定义有参构造
name = n; // 为类中的属性赋值(初始化)
age = a; // 为类中的属性赋值(初始化)
}
public void tell() {
System.out.println("姓名:" + name + ",年龄:" + age);
}
}
public class JavaDemo { // 主类
public static void main(String[] args) {
// 1、对象初始化准备
Person per = new Person("张三", 18); // 声明并实例化对象
// 2、对象的使用
per.tell(); // 进行方法的调用
}
}
疑问:为什么构造方法上不允许设置返回值类型?
程序编译器是根据代码结构来进行编译处理的,执行时也是根据代码结构来处理的。
因此如果设置了返回值类型,那么构造方法的结构与普通方法的结构完全相同,这样编译器会认为此方法时一个普通方法。
普通方法与构造方法最大的区别:构造方法是在类对象实例化时调用的,而普通方法是在类对象实例化产生之后才可以调用的。
public class JavaDemo { // 主类
public static void main(String[] args) {
new Person("张三", 10).tell(); // 通过匿名对象直接进行方法的调用
}
}
使用this的三类结构:
在Java程序中,“{}”是作为一个结构体的边界符,所以在程序里面当进行变量(参数 / 属性)使用时,都会以“{}”作为查找边界。
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age
}
public void tell() {
System.out.println("姓名:" + this.name + ",年龄:" + this.age);
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.setName(name); // 调用本类方法
setAge(age); // 加不加“this.”都表示调用本类方法
}
public void tell() {
System.out.println("姓名:" + this.name + ",年龄:" + this.age);
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
}
class Person {
private String name;
private int age;
public Person() {
System.out.println("*** 一个新的Person类对象实例化了。");
}
public Person(String name) {
this(); // 调用本类无参构造
this.name = name;
}
public Person(String name, int age) {
this(name); // 调用本类单参构造
this.age = age;
}
public void tell() {
System.out.println("姓名:" + this.name + ",年龄:" + this.age);
}
// setter、getter略
}
评价代码的好坏:
本类构造方法的互相调用
class Emp {
private long empno; // 编号
private String ename; // 姓名
private String dept; // 部门
private double salary; // 工资
public Emp() {
this(1000, "无名氏", null, 0.0);
}
public Emp(long empno) {
this(empno, "新员工", "未定", 0.0);
}
public Emp(long empno, String ename, String dept) {
this(empno, ename, dept, 2500.00);
}
public Emp(long empno, String ename, String dept, double salary) {
this.empno = empno;
this.ename = ename;
this.dept = dept;
this.salary = salary;
}
// setter、getter略
public String getInfo() {
return "编号:" + this.empno +
",姓名:" + this.ename +
",部门:" + this.dept +
",工资:" + this.salary;
}
}
public class JavaDemo { // 主类
public static void main(String[] args) {
Emp emp = new Emp(7369L, "史密斯", "财务部");
System.out.println(emp.getInfo());
}
}
class Dept { // 类名称可以明确描述出某类事物
private long deptno;
private String dname;
private String loc;
public Dept() {} // 必须提供无参构造
public Dept(long deptno, String dname, String loc) {
this.deptno = deptno;
this.dname = dname;
this.loc = loc;
}
public void setDeptno(long deptno) {
this.deptno = deptno;
}
public void setDname(String dname) {
this.dname = dname;
}
public void setLoc(String loc) {
this.loc = loc;
}
public String getInfo() {
return "部门编号:" + this.deptno +
",部门名称:" + this.dname +
",部门位置:" + this.loc;
}
}
public class JavaDemo { // 主类
public static void main(String[] args) {
Dept dept = new Dept(10, "技术部", "北京");
System.out.println(dept.getInfo());
}
}
class Person {
private String name;
private int age;
String country = "中华民国";
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// setter、getter略
public String getInfo() {
return "姓名:" + this.name +
",年龄:" + this.age +
",国家:" + this.coutry;
}
}
public class JavaDemo { // 主类
public static void main(String[] args) {
Person perA = new Person("张三", 10);
Person perB = new Person("李四", 10);
Person perC = new Person("王五", 11);
System.out.println(perA.getInfo());
System.out.println(perB.getInfo());
System.out.println(perC.getInfo());
}
}
class Person {
private String name;
private int age;
static String country = "中华民国"; //
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// setter、getter略
public String getInfo() {
return "姓名:" + this.name +
",年龄:" + this.age +
",国家:" + this.coutry;
}
}
public class JavaDemo { // 主类
public static void main(String[] args) {
Person perA = new Person("张三", 10);
Person perB = new Person("李四", 10);
Person perC = new Person("王五", 11);
perA.country = "中华人民共和国";
System.out.println(perA.getInfo());
System.out.println(perB.getInfo());
System.out.println(perC.getInfo());
}
}
Person.country = "中华人民共和国";
class Person {
private String name;
private int age;
static String country = "中华民国"; //
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void setCountry(String c) { // static方法
country = c;
}
// setter、getter略
public String getInfo() {
return "姓名:" + this.name +
",年龄:" + this.age +
",国家:" + this.coutry;
}
}
public class JavaDemo { // 主类
public static void main(String[] args) {
Person.setCountry("中华人民共和国");
Person per = new Person("张三", 10);
System.out.println(per.getInfo());
}
}
class Book {
private String title;
private static int count = 0;
public Book(String title) {
this.title = title;
count ++;
System.out.println("第" + count + "本图书创建出来了。");
}
}
public class JavaDemo {
public static void main(String[] args) {
new Book("JAVA");
new Book("JSP");
new Book("Spring");
}
}
class Book {
private String title;
private static int count = 0;
public Book() {
this("NOTITLE - " + count++);
}
public Book(String title) {
this.title = title;
}
public String getTitle() {
return this.title;
}
}
public class JavaDemo {
public static void main(String[] args) {
System.out.println(new Book("JAVA").getTitle());
System.out.println(new Book("JSP").getTitle());
System.out.println(new Book("Spring").getTitle());
System.out.println(new Book().getTitle()); // 输出 NOTITLE - 0
System.out.println(new Book().getTitle()); // 输出 NOTITLE - 1
}
}
代码块:在程序中使用“{}”定义的结构。
分类:
普通代码块:
public class JavaDemo {
public static void main(String[] args) {
if (true) { // 条件一定满足
int x = 10; // 局部变量
System.out.println("x=" + x);
}
int x = 100; // 全局变量
System.out.println("x=" + x);
}
}
按Java程序的开发标准,相同名称的变量是不能够在同一个方法中重复声明的,但由于此时由不同的分界描述。
public class JavaDemo {
public static void main(String[] args) {
{ // 普通代码块
int x = 10; // 局部变量
System.out.println("x=" + x);
}
int x = 100; // 全局变量
System.out.println("x=" + x);
}
}
class Person {
public Person() {
System.out.println("【构造方法】Person类构造方法执行");
}
{
System.out.println("【构造块】Person类构造块执行");
}
}
public class JavaDemo {
public static void main(String[] args) {
new Person();
new Person();
new Person();
}
}
class Person {
public Person() {
System.out.println("【构造方法】Person类构造方法执行");
}
static { // 非主类中定义静态块
System.out.println("【静态块】静态块执行");
}
{
System.out.println("【构造块】Person类构造块执行");
}
}
public class JavaDemo {
public static void main(String[] args) {
new Person();
new Person();
new Person();
}
}
public class JavaDemo {
static { // 主类中定义静态块
System.out.println("******** 程序初始化 ********");
}
public static void main(String[] args) {
System.out.println("——————");
}
}
class Address {
private String country;
private String province;
private String city;
private String street;
private String zipcode;
public Address() {}
public Address(String country, String province, String city, String street, String zipcode) {
this.country = country;
this.province = province;
this.city = city;
this.street = street;
this.zipcode = zipcode;
}
public String getInfo() {
return "国家:" + this.country + ",省份:" + this.province +
",城市:" + this.city + ",街道:" + this.street +
",邮编:" + this.zipcode;
}
public void setCountry(String country) {
this.country = country;
}
public void setProvince(String province) {
this.province = province;
}
public void setCity(String city) {
this.city = city;
}
public void setStreet(String street) {
this.street = street;
}
public void setZipcode(String zipcode) {
this.zipcode = zipcode;
}
public String getCountry() {
return this.country;
}
public String getProvince() {
return this.province;
}
public String getCity() {
return this.city;
}
public String getStreet() {
return this.street;
}
public String getZipcode() {
return this.zipcode;
}
}
public class JavaDemo {
public static void main(String[] args) {
System.out.println(new Address("中华人民共和国", "北京", "北京", "天安门街道", "10001").getInfo());
}
}
class Employee {
private long empno;
private String ename;
private double salary;
private double rate;
public Employee() {}
public Employee(long empno, String ename, double salary, double rate) {
this.empno = empno;
this.ename = ename;
this.salary = salary;
this.rate = rate;
}
public double salaryIncValue() { // 得到薪水增长额度
return this.salary * this.rate;
}
public double salaryIncResult() {
return this.salary * (1 + this.rate);
}
// setter、getter略
public String getInfo() {
return "雇员编号:" + this.empno + ",雇员姓名:" + this.ename +
",基本薪水:" + this.salary + ",薪水增长率:" + this.rate;
}
}
public class JavaDemo {
public static void main(String[] args) {
Employee emp = new Employee(7369L, "史密斯", 3000.0, 0.3);
System.out.println(emp.getInfo());
System.out.println("工资调整额度:" + emp.salaryIncValue());
System.out.println("上调后的工资:" + emp.salaryIncResult());
}
}
class Dog {
private String name;
private String color;
private int age;
public Dog() {}
public Dog(String name, String color, int age) {
this.name = name;
this.color = color;
this.age = age;
}
// setter、getter略
public String getInfo() {
return "狗的名字:" + this.name + ",狗的颜色:" + this.color + ",狗的年龄:" + this.age;
}
}
public class JavaDemo {
public static void main(String[] args) {
Dog dog = new Dog("高高", "黑色", 1);
System.out.println(dog.getInfo());
}
}
class Account {
private String name;
private double balance;
public Account() {}
public Account(String name) {
this(name, 0.0);
}
public Account(String name, double balance) {
this.name = name;
this.balance = balance;
}
// setter、getter略
public double getBalance() {
return this.balance;
}
public String getInfo() {
return "账户名称:" + this.name + ",余额:" + this.balance;
}
}
public class JavaDemo {
public static void main(String[] args) {
Account account = new Account("啊嘟嘟", 9000000.00);
System.out.println(account.getInfo());
System.out.println(account.getBalance());
}
}
class User {
private String uid;
private String password;
private static int count = 0;
public User() {
this("NO-UID", "123456");
}
public User(String uid) {
this(uid, "123456");
}
public User(String uid, String password) {
this.uid = uid;
this.password = password;
count ++; // 个数追加
}
public static int getCount() { // 获取用户个数
return count;
}
// setter、getter略
public String getInfo() {
return "用户名:" + this.uid + ",密码:" + this.password;
}
}
public class JavaDemo {
public static void main(String[] args) {
User userA = new User();
User userB = new User("小强");
User userC = new User("大强", "我不行");
System.out.println(userA.getInfo());
System.out.println(userB.getInfo());
System.out.println(userC.getInfo());
System.out.println("用户个数:" + User.getCount());
}
}
class Book {
private int bid; // 编号
private String title; // 书名
private double price; // 价格
private static int count = 0;
public Book(String title, double price) {
this.bid = count + 1;
this.title = title;
this.price = price;
count ++;
}
public static int getCount() { // 获取用户个数
return count;
}
// setter、getter略
public String getInfo() {
return "图书编号:" + this.bid + ",图书名称:" + this.title + ",图书价格:" + this.price;
}
}
public class JavaDemo {
public static void main(String[] args) {
Book book1 = new Book("Java", 89.2);
Book book2 = new Book("Oracle", 79.2);
System.out.println(book1.getInfo());
System.out.println(book2.getInfo());
System.out.println("图书总册数:" + Book.getCount());
}
}
数组的本质:一组相关变量的集合。
数组的初始化:
数组的使用:
public class ArrayDemo{
public static void main(String[] args) {
int[] data = new int[3]; // 动态初始化数组
for (int i = 0; i < data.length; i++) {
System.out.println(data[i]);
}
}
}
public class ArrayDemo{
public static void main(String[] args) {
int[] data = new int[3]; // 动态初始化数组
data[0] = 11; // 为数组设置内容
data[1] = 23; // 为数组设置内容
data[2] = 56; // 为数组设置内容
for (int i = 0; i < data.length; i++) {
System.out.println(data[i]);
}
}
}
public class ArrayDemo{
public static void main(String[] args) {
int[] data = new int[] {11, 23, 56}; // 静态初始化数组
for (int i = 0; i < data.length; i++) {
System.out.println(data[i]);
}
}
}
public class ArrayDemo{
public static void main(String[] args) {
int[] data = new int[3]; // 动态初始化数组
data[0] = 10; // 为数组设置内容
data[1] = 20; // 为数组设置内容
data[2] = 30; // 为数组设置内容
for (int i = 0; i < data.length; i ++) {
System.out.println(data[i]);
}
}
}
public class ArrayDemo{
public static void main(String[] args) {
int[] data = new int[] {10, 20, 30}; // 静态初始化数组
int temp[] = data; // 引用传递
temp[0] = 99;
for (int i = 0; i < data.length; i ++) {
System.out.println(data[i]);
}
}
}
public class ArrayDemo{
public static void main(String[] args) {
int[] data = null; // 未开辟堆内存空间
System.out.println(data[0]); // 出现”NullPointerException“的异常
}
}
public class ArrayDemo{
public static void main(String[] args) {
int[] data = new int[] {1, 2, 3, 4, 5};
for (int temp : data) { // 自动循环,将data数组的每一个元素交给temp变量
System.out.println(temp);
}
}
}
二维数组的定义:需要通过两个下标(行下标 + 列下标)来共同定义的数组。
二维数组的初始化:
动态初始化:
数据类型[][] 数组名称 = new 数据类型[行个数][列个数];
静态初始化:
数据类型[][] 数组名称 = new 数据类型[][] {
{数组,数据,...},{数组,数据,...},...};
public class ArrayDemo{
public static void main(String[] args) {
int[][] data = new int[][] {
{1,2,3,4,5}, {1,2,3}, {5,6,7,8}
};
for (int i = 0; i < data.length; i ++) { // 传统for循环
for (int j = 0; j < data[i].length; j ++) {
System.out.println("data[" + i + "][" + j + "] = " + data[i][j]);
}
System.out.println(); // 换行
}
}
}
索引(下标) | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | 5 |
1 | 1 | 2 | 3 | ||
0 | 5 | 6 | 7 | 8 |
public class ArrayDemo{
public static void main(String[] args) {
int[][] data = new int[][] {
{1,2,3,4,5}, {1,2,3}, {5,6,7,8}
};
for (int[] arr : data) { // foreach循环
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println(); // 换行
}
}
}
对于引用数据类型而言,主要的特点就是可以与方法进行引用传递,而由于数组也属于引用数据类型,所以自然也可以通过方法进行引用传递的工作。
通过方法来接收一个数组
public class ArrayDemo{
public static void main(String[] args) {
int[] data = new int[] {1,2,3,4,5};
printArray(data);
}
public static void printArray(int[] arr) { // 接收一个int型的数组
for (int temp : arr) {
System.out.print(temp + " ");
}
System.out.println();
}
}
public class ArrayDemo{
public static void main(String[] args) {
int[] data = initArray();
printArray(data);
}
public static int[] initArray() { //返回一个int型的数组
int[] arr = new int[] {1,2,3,4,5};
return arr;
}
public static void printArray(int[] arr) { // 接收一个int型的数组
for (int temp : arr) {
System.out.print(temp + " ");
}
System.out.println();
}
}
public class ArrayDemo{
public static void main(String[] args) {
int[] data = new int[] {1,2,3,4,5};
changeArray(data);
printArray(data);
}
public static void changeArray(int[] arr) { //返回一个int型的数组
for (int i = 0; i < arr.length; i ++) {
arr[i] *= 2; // 改变数组内容
}
}
public static void printArray(int[] arr) { // 接收一个int型的数组
for (int temp : arr) {
System.out.print(temp + " ");
}
System.out.println();
}
}
class ArrayUtil { // 操作工具的类
private int sum; // 保存总和
private double avg; // 保存平均值
private int max; // 保存最大值
private int min; // 保存最小值
public ArrayUtil(int[] arr) { // 进行数组计算
this.max = arr[0]; // 假设第一个是最大值
this.min = arr[0]; // 假设第一个是最小值
for (int i = 0; i < arr.length; i ++) {
if (arr[i] > this.max) this.max = arr[i];
if (arr[i] < this.min) this.min = arr[i];
this.sum += arr[i];
}
this.avg = this.sum / arr.length;
}
public int getSum() {
return this.sum;
}
public double getAvg() {
return this.avg;
}
public int getMax() {
return this.max;
}
public int getMin() {
return this.min;
}
}
public class ArrayDemo {
public static void main(String[] args) {
int[] data = new int[] {1,2,3,4,5};
ArrayUtil util = new ArrayUtil(data); // 数据计算
System.out.println("数组内容的总和:" + util.getSum());
System.out.println("数组内容的平均值:" + util.getAvg());
System.out.println("数组内容的最大值:" + util.getMax());
System.out.println("数组内容的最小值:" + util.getMin());
}
}
class ArrayUtil {
// 进行数组的排序处理
public static void sort(int[] arr) {
for (int i = 0; i < arr.length; i ++) {
for (int j = 0; j < arr.length-i-1; j ++) {
if (arr[j] > arr[j + 1]) { // 交换数据
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
// 进行数组的打印
public static void print(int[] arr) {
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
}
}
public class ArrayDemo {
public static void main(String[] args) {
int[] arr = new int[] {8,9,0,2,3,5,10,7,6,1};
ArrayUtil.sort(arr); // 排序
ArrayUtil.print(arr); // 打印
}
}
数组转置的定义:指进行数据内容前后转置的处理,即:首尾交换。
数组转置的做法:
做法一:定义一个新的数组,然后按照逆序的方式保存(会产生无用的垃圾空间)
public class ArrayDemo {
public static void main(String[] args) {
int[] data = new int[] {1,2,3,4,5,6,7,8,9};
int[] temp = new int[data.length]; // 用于保存转置后内容的数组,第二个数组
int foot = temp.length - 1; // 第二个数组的脚标
for (int i = 0; i < data.length; i ++) {
temp[foot --] = data[i];
}
data = temp;
ArrayUtil.print(data); // 打印
}
}
做法二:在一个数组上进行转置
public class ArrayDemo {
public static void main(String[] args) {
int[] data = new int[] {1,2,3,4,5,6,7,8,9};
int center = data.length / 2; // 确定转换次数
int head = 0; // 操作头脚标
int tail = data.length - 1; // 操作尾脚标
for (int i = 0; i < center; i ++) {
int temp = data[head];
data[head] = data[tail];
data[tail] = temp;
head ++;
tail --;
}
ArrayUtil.print(data); // 打印
}
}
两种做法的对比:
class ArrayUtil {
// 进行数组的转置处理
public static void reverse(int arr[]) {
int center = arr.length / 2; // 确定转换次数
int head = 0; // 操作头脚标
int tail = arr.length - 1; // 操作尾脚标
for (int i = 0; i < center; i ++) {
int temp = arr[head];
arr[head] = arr[tail];
arr[tail] = temp;
head ++;
tail --;
}
}
// 进行数组的打印
public static void print(int arr[]) {
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
}
}
public class ArrayDemo {
public static void main(String[] args) {
int[] arr = new int[] {1,2,3,4,5,6,7,8,9};
ArrayUtil.reverse(arr); // 转置
ArrayUtil.print(arr); // 打印
}
}
public class ArrayDemo {
public static void main(String[] args) {
int[] arr = new int[] {23,12,1,234,2,6,12,34,56};
java.util.Arrays.sort(arr);
ArrayUtil.print(arr); // 输出 1 2 6 12 12 23 34 56 234
}
}
public class ArrayDemo {
public static void main(String[] args) {
int[] arr1 = new int[] {1,2,3,4,5,6,7,8,9};
int[] arr2 = new int[] {11,22,33,44,55,66,77,88,99};
System.arraycopy(arr1, 5, arr2, 3, 3); // 使用系统函数
ArrayUtil.print(arr2); // 输出 11 22 33 6 7 8 77 88 99
}
}
class ArrayUtil {
// 进行数组的拷贝处理
public static void copy(int[] srcArr, int srcIndex, int[] dscArr, int dscIndex, int len) {
for (int i = 0; i < len; i ++) {
dscArr[dscIndex ++] = srcArr[srcIndex ++];
}
}
// 进行数组的打印
public static void print(int[] arr) {
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
}
}
public class ArrayDemo {
public static void main(String[] args) {
int[] arr1 = new int[] {1,2,3,4,5,6,7,8,9};
int[] arr2 = new int[] {11,22,33,44,55,66,77,88,99};
ArrayUtil.copy(arr1, 5, arr2, 3, 3); // 使用自定义函数
ArrayUtil.print(arr2); // 输出 11 22 33 6 7 8 77 88 99
}
}
class ArrayUtil {
public static int sum(int ... data) { // 变种数组
int sum = 0;
for (int temp : data) {
sum += temp;
}
return sum;
}
}
public class ArrayDemo {
public static void main(String[] args) {
System.out.println(ArrayUtil.sum(1,2,3));
System.out.println(ArrayUtil.sum(new int[] {1,2,3}));
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getInfo() {
return "姓名:" + this.name + ",年龄:" + this.age;
}
// setter、getter略
}
public class ArrayDemo {
public static void main(String[] args) {
Person[] person = new Person[3]; // 对象数组,动态初始化
for (int i = 0; i < person.length; i ++) {
System.out.println(person[i]); // 输出3行null
}
}
}
public class ArrayDemo {
public static void main(String[] args) {
Person[] person = new Person[3]; // 对象数组,动态初始化
person[0] = new Person("张三", 20);
person[1] = new Person("李四", 20);
person[2] = new Person("王五", 20);
for (int i = 0; i < person.length; i ++) {
System.out.println(person[i]); // 输出3行内存地址
}
}
}
public class ArrayDemo {
public static void main(String[] args) {
Person[] person = new Person[3]; // 对象数组,动态初始化
person[0] = new Person("张三", 20);
person[1] = new Person("李四", 20);
person[2] = new Person("王五", 20);
for (int i = 0; i < person.length; i ++) {
System.out.println(person[i]); // 正常输出3行姓名和年龄
}
}
}
public class ArrayDemo {
public static void main(String[] args) {
Person[] person = new Person[] { // 对象数组,静态初始化
new Person("张三", 20),
new Person("李四", 20),
new Person("王五", 20),
};
for (int i = 0; i < person.length; i ++) {
System.out.println(person[i]); // 正常输出3行姓名和年龄
}
}
}
class Car {
private String name;
private double price;
private Person person; // 车应该属于一个人
public Car(String name, double price) {
this.name = name;
this.price = price;
}
public void setPerson(Person person) {
this.person = person;
}
public Person getPerson() {
return this.person;
}
public String getInfo() {
return "汽车品牌型号:" + this.name + ",汽车价值:" + this.price;
}
}
class Person {
private String name;
private int age;
private Car car; // 人有一辆车
public Person(String name, int age) {
this.name = name ;
this.age = age;
}
public void setCar(Car car) {
this.car = car;
}
public Car getCar() {
return this.car;
}
public String getInfo() {
return "姓名:" + this.name + ",年龄:" + this.age;
}
}
public class ArrayDemo {
public static void main(String[] args) {
// 第一步:声明对象并设置彼此的关系
Person person = new Person("林强", 29);
Car car = new Car("宾利", 8000000.00);
person.setCar(car); // 一个人有一辆车
car.setPerson(person); // 一辆车属于一个人
// 第二步:根据关系获取数据
System.out.println(person.getCar().getInfo()); // 代码链
System.out.println(car.getPerson().getInfo());
}
}
class Car {
private String name;
private double price;
private Person person; // 车应该属于一个人
public Car(String name, double price) {
this.name = name;
this.price = price;
}
public void setPerson(Person person) {
this.person = person;
}
public Person getPerson() {
return this.person;
}
public String getInfo() {
return "汽车品牌型号:" + this.name + ",汽车价值:" + this.price;
}
}
class Person {
private String name;
private int age;
private Car car; // 人有一辆车
private Person[] children; // 有多个孩子
public Person(String name, int age) {
this.name = name ;
this.age = age;
}
public void setChildren(Person[] children) {
this.children = children;
}
public Person[] getChildren() {
return this.children;
}
public void setCar(Car car) {
this.car = car;
}
public Car getCar() {
return this.car;
}
public String getInfo() {
return "姓名:" + this.name + ",年龄:" + this.age;
}
}
public class ArrayDemo {
public static void main(String[] args) {
// 第一步:声明对象并设置彼此的关系
Person person = new Person("林强", 29);
Person childA = new Person("吴硕", 18);
Person childB = new Person("郭仁义", 19);
childA.setCar(new Car("宝马", 300000.00)); // 匿名Car对象
childB.setCar(new Car("法拉利", 1500000.00)); // 匿名Car对象
person.setChildren(new Person[] {childA, childB}); // 一个人有多个孩子
Car car = new Car("宾利", 8000000.00);
person.setCar(car); // 一个人有一辆车
car.setPerson(person); // 一辆车属于一个人
// 第二步:根据关系获取数据
System.out.println(person.getCar().getInfo()); // 代码链
System.out.println(car.getPerson().getInfo());
System.out.println("—————— 我是一条分界线 ——————");
// 根据人找到所有的孩子以及孩子对应的汽车
Person[] children = person.getChildren();
for (int i = 0; i < person.getChildren().length; i ++) {
Person child = children[i];
System.out.println("\t" + child.getInfo());
System.out.println("\t\t" + child.getCar().getInfo());
}
}
}
class Computer {
private Display[] displays;
private Host host;
}
class Display {}
class Host {
private Mainboard mainboard;
private Keyword keyword;
private Mouse mouse;
}
class Mainboard {
private Memory[] memorys;
private Cpu[] cpus;
private Gpu gpu;
private Hdd[] hdd;
}
class Keyword {}
class Mouse {}
class Memory {}
程序类的定义实际上和数据表的定义相差不大。
数据表与简单Java类之间的基本映射关系:
案例:部门表与雇员表
表的分析:
程序的分析:
实现步骤:
第一步:分别定义Dept、Emp两个实体类
class Dept {
private long deptno;
private String dname;
private String loc;
public Dept(long deptno, String dname, String loc) {
this.deptno = deptno;
this.dname = dname;
this.loc = loc;
}
// setter、getter、无参构造略
public String getInfo() {
return "【部门信息】部门编号:" + this.deptno +
",部门名称:" + this.dname +
",部门位置:" + this.loc;
}
}
class Emp {
private long empno;
private String ename;
private String job;
private double sal;
private double comm;
public Emp(long empno, String ename, String job, double sal, double comm) {
this.empno = empno;
this.ename = ename;
this.job = job;
this.sal = sal;
this.comm = comm;
}
// setter、getter、无参构造略
public String getInfo() {
return "【雇员信息】雇员编号:" + this.empno +
",雇员姓名:" + this.ename +
",雇员职位:" + this.job +
",基本工资:" + this.sal +
",佣金:" + this.comm;
}
}
第二步:配置所有的关联字段
class Dept {
private Emp[] emps;
public void setEmps(Emp[] emps) {
this.emps = emps;
}
public Emp[] getEmps() {
return this.emps;
}
}
class Emp {
private Dept dept; // 所属部门
private Emp mgr; // 所属领导
public void setDept(Dept dept) {
this.dept = dept;
}
public Dept getDept() {
return this.dept;
}
public void setMgr(Emp mgr) {
this.mgr = mgr;
}
public Emp getMgr() {
return this.mgr;
}
}
实际实现:
public class ArrayDemo {
public static void main(String[] args) {
// 第一步:根据关系进行类的定义
Dept dept = new Dept(10, "财务部", "上海");
Emp empA = new Emp(7369L, "SMITH", "CLERK", 800.00, 0.0);
Emp empB = new Emp(7566L, "FORD", "MANAGER", 2450.00, 0.0);
Emp empC = new Emp(7839L, "KING", "PRESIDENT", 5000.00, 0.0);
// 为对象进行关联设置
empA.setDept(dept); // 设置雇员与部门的关联
empB.setDept(dept); // 设置雇员与部门的关联
empC.setDept(dept); // 设置雇员与部门的关联
empA.setMgr(empB); // 设置雇员与领导的关联
empB.setMgr(empC); // 设置雇员与领导的关联
dept.setEmps(new Emp[] {empA, empB, empC}; // 部门与雇员
// 第二步:根据关系获取数据
System.out.println(dept.getInfo());
Emp[] emps = dept.getEmps();
for (int i = 0; i < emps.length; i ++) {
System.out.println("\t" + emps[i].getInfo());
Emp mgr = emps[i].getMgr();
if (null != mgr) {
System.out.println("\t\t" + mgr.getInfo());
}
}
System.out.println("————————————————");
System.out.println(empB.getDept().getInfo()); // 根据雇员获取部门信息
System.out.println(empB.getMgr().getInfo()); // 根据雇员获取领导信息
}
}
实际项目开发实现步骤:
class Item {
private long iid;
private String title;
private Subitem[] subitems;
public Item(long iid, String title) {
this.iid = iid;
this.title = title;
}
public void setSubitems(Subitem[] subitems) {
this.subitems = subitems;
}
public Subitem[] getSubitems() {
return this.subitems;
}
public String getInfo() {
return "【分类信息】iid = " + this.iid + ",title = " + this.title;
}
}
class Subitem {
private long sid;
private String title;
private Item item;
public Subitem(long sid, String title) {
this.sid = sid;
this.title = title;
}
public void setItem(Item item) {
this.item = item;
}
public Item getItem() {
return this.item;
}
public String getInfo() {
return "【子分类信息】sid = " + this.sid + ",title = " + this.title;
}
}
public class JavaDemo {
public static void main(String[] args) {
// 第一步:根据结构设置对象数据
Item item = new Item(1L, "图书");
Subitem[] subitems = new Subitem[] {
new Subitem(10L, "编程图书"),
new Subitem(10L, "图形图像类图书"),
};
item.setSubitems(subitems); // 一个分类下有多个子分类
for (int i = 0; i < subitems.length; i ++) {
subitems[i].setItem(item);
}
// 第二步:根据要求获取数据
System.out.println(item.getInfo());
subitems = item.getSubitems();
for (int i = 0; i < subitems.length; i ++) {
System.out.println("\t" + subitems[i].getInfo());
}
}
}
class Member {
private String mid;
private String name;
private Product[] products;
public Member(String mid, String name) {
this.mid = mid;
this.name = name;
}
public void setProducts(Product[] products) {
this.products = products;
}
public Product[] getProducts() {
return this.products;
}
public String getInfo() {
return "【用户信息】mid = " + this.mid + ",name = " + this.name;
}
}
class Product {
private long pid;
private String title;
private double price;
private Member[] members;
public Product(long pid, String title, double price) {
this.pid = pid;
this.title = title;
this.price = price;
}
public void setMembers(Member[] members) {
this.members = members;
}
public Member[] getMembers() {
return this.members;
}
public String getInfo() {
return "【商品信息】pid = " + this.pid + ",title = " + this.title + ",price = " + this.price;
}
}
public class JavaDemo {
public static void main(String[] args) {
// 第一步:根据结构设置对象数据
Member memA = new Member("pika", "皮卡");
Member memB = new Member("pikaqiu", "皮卡丘");
Product proA = new Product(1L, "Java开发图书", 79.8);
Product proB = new Product(2L, "非常大的耳机", 2309.8);
Product proC = new Product(3L, "小米手机", 3000.8);
memA.setProducts(new Product[] {proA, proB, proC});
memB.setProducts(new Product[] {proA});
proA.setMembers(new Member[] {memA, memB});
proB.setMembers(new Member[] {memA});
// 第二步:根据要求获取数据
System.out.println("———————— 根据用户查看浏览商品信息 ————————");
System.out.println(memA.getInfo());
Product[] memA_products = memA.getProducts();
for (int i = 0; i < memA_products.length; i ++) {
System.out.println("\t" + memA_products[i].getInfo());
}
System.out.println("———————— 根据商品找到被浏览的记录 ————————");
System.out.println(proA.getInfo());
Member[] proA_members = proA.getMembers();
for (int i = 0; i < proA_members.length; i ++) {
System.out.println("\t" + proA_members[i].getInfo());
}
}
}
class Member {
private String mid;
private String name;
private Role[] roles;
public Member(String mid, String name) {
this.mid = mid;
this.name = name;
}
public void setRoles(Role[] roles) {
this.roles = roles;
}
public Role[] getRoles() {
return this.roles;
}
public String getInfo() {
return "【用户信息】mid = " + this.mid + ",name = " + this.name;
}
}
class Role {
private long rid;
private String title;
private Member[] members;
private Privilege[] privileges;
public Role(long rid, String title) {
this.rid = rid;
this.title = title;
}
public void setMembers(Member[] members) {
this.members = members;
}
public Member[] getMembers() {
return this.members;
}
public void setPrivileges(Privilege[] privileges) {
this.privileges = privileges;
}
public Privilege[] getPrivileges() {
return this.privileges;
}
public String getInfo() {
return "【角色信息】rid = " + this.rid + ",title = " + this.title;
}
}
class Privilege {
private long pid;
private String title;
private Role role;
public Privilege(long pid, String title) {
this.pid = pid;
this.title = title;
}
public void setRole(Role role) {
this.role = role;
}
public Role getRole() {
return this.role;
}
public String getInfo() {
return "【权限信息】pid = " + this.pid + ",title = " + this.title;
}
}
public class JavaDemo {
public static void main(String[] args) {
// 第一步:根据结构设置对象数据
Member memA = new Member("pika", "皮卡");
Member memB = new Member("pikaqiu", "皮卡丘");
Role roleA = new Role(1L, "系统配置");
Role roleB = new Role(2L, "备份管理");
Role roleC = new Role(3L, "认识管理");
Privilege priA = new Privilege(1000L, "系统初始化");
Privilege priB = new Privilege(1001L, "系统还原");
Privilege priC = new Privilege(1002L, "系统环境修改");
Privilege priD = new Privilege(2000L, "备份员工数据");
Privilege priE = new Privilege(2001L, "备份部门数据");
Privilege priF = new Privilege(2002L, "备份公文数据");
Privilege priG = new Privilege(3000L, "增加员工");
Privilege priH = new Privilege(3001L, "编辑员工");
Privilege priI = new Privilege(3002L, "浏览员工");
Privilege priJ = new Privilege(3003L, "员工离职");
// 增加角色与权限的对应关系
roleA.setPrivileges(new Privilege[] {priA, priB, priC});
roleB.setPrivileges(new Privilege[] {priD, priE, priF});
roleC.setPrivileges(new Privilege[] {priG, priH, priI, priJ});
// 增加权限与角色对应关系
priA.setRole(roleA);
priB.setRole(roleA);
priC.setRole(roleA);
priD.setRole(roleB);
priE.setRole(roleB);
priF.setRole(roleB);
priG.setRole(roleC);
priH.setRole(roleC);
priI.setRole(roleC);
priJ.setRole(roleC);
// 增加用户与角色的对应关系
memA.setRoles(new Role[] {roleA, roleB});
memB.setRoles(new Role[] {roleA, roleB, roleC});
roleA.setMembers(new Member[] {memA, memB});
roleB.setMembers(new Member[] {memA, memB});
roleC.setMembers(new Member[] {memB});
// 第二步:根据要求获取数据
System.out.println("———————— 通过用户查找信息 ————————");
System.out.println(memB.getInfo());
Role[] memB_roles = memB.getRoles();
for (int i = 0; i < memB_roles.length; i ++) {
System.out.println("\t" + memB_roles[i].getInfo());
Privilege[] memB_roles_pris = memB_roles[i].getPrivileges();
for (int j = 0; j < memB_roles_pris.length; j ++) {
System.out.println("\t\t" + memB_roles_pris[j].getInfo());
}
}
System.out.println("———————— 通过角色查找信息 ————————");
System.out.println(roleB.getInfo());
System.out.println("\t浏览此角色下的所有权限信息:");
Privilege[] roleB_pris = roleB.getPrivileges();
for (int j = 0; j < roleB_pris.length; j ++) {
System.out.println("\t\t" + roleB_pris[j].getInfo());
}
System.out.println("\t浏览此角色下的所有用户信息:");
Member[] roleB_mems = roleB.getMembers();
for (int j = 0; j < roleB_mems.length; j ++) {
System.out.println("\t\t" + roleB_mems[j].getInfo());
}
System.out.println("———————— 通过权限查找信息 ————————");
System.out.println(priB.getInfo());
Member[] priB_role_mems = priB.getRole().getMembers();
for (int j = 0; j < priB_role_mems.length; j ++) {
System.out.println("\t\t" + priB_role_mems[j].getInfo());
}
}
}
在Java程序里,使用”"“(双引号)对字符串进行定义,利用”+“(加号)来实现字符串的连接。
String类的对象实例化
直接赋值
public class StringDemo {
public static void main(String[] args) {
String str = "pikaqiu"; // 直接赋值进行实例化
System.out.println(str);
}
}
构造方法
public class StringDemo {
public static void main(String[] args) {
String str = new String("pikaqiu"); // 使用构造方法进行实例化
System.out.println(str);
}
}
String类之所以能保存字符串的主要原因是其中定义了一个数组,也就是说字符串里的每一个字符都是保存在数组中。
JDK1.8与JDK1.9String支持类的对比:
JDK1.8的String支持类 | JDK1.9的String支持类 |
---|---|
String类保存的数组类型:
JDK1.8:String类保存的是字符数组;
private final char[] value;
JDK1.9:String类保存的是字节数组。
private final byte[] value;
字符串就是对数组的一种特殊包装应用。
public class StringDemo {
public static void main(String[] args) {
String strA = "pikaqiu";
String strB = new String("pikaqiu");
System.out.println(strA == strB); // 输出 false
System.out.println(strA.equals(strB)); // 输出 true
}
}
public class StringDemo {
public static void main(String[] args) {
String str = "pikaqiu";
System.out.println("pikaqiu".equals(str)); // 输出 true。此处的“pikaqiu”则是字符串常量
}
}
String类对象的实例化方式:
直接赋值:
String str = "mldn";
String(常量)池
public class StringDemo {
public static void main(String[] args) {
String strA = "mldn";
String strB = "mldnjava";
String strC = "mldn";
System.out.println(strA == strC); // 输出 true(判断的是内存地址)
}
}
在采用直接赋值的处理过程中,对于字符串而言,可以实现池数据的自动保存,这样再有相同数据定义时,可以减少对象的产生,以提升操作性能。
构造方法:
new String("mldn");
此时会开辟两块堆内存空间,而后只会使用一块,而另一个由于字符串常量所定义的匿名对象将称为垃圾对象。
实例化方式对比:
利用构造方法实例化的对象,会产生专用的内存空间,但在String类中,也提供了手工入池的方法:
public String intern();
public class StringDemo {
public static void main(String[] args) {
String strA = "pikaqiu";
String strB = new String("pikaqiu").intern(); // 使用intern()手工入池的方法
System.out.println(strA == strB); // 输出 true
}
}
面试题:请解释String类两种对象实例化方式的区别?
对象池的主要目的:实现数据的共享处理。
Java对象(常量)池的分类:
public class StringDemo {
public static void main(String[] args) {
String strA = "pikaqiu";
String strB = "pi" + "ka" + "qiu";
System.out.println(strA == strB); // 输出 true
}
}
public class StringDemo {
public static void main(String[] args) {
String info = "ka";
String strA = "pikaqiu";
String strB = "pi" + info + "qiu";
System.out.println(strA == strB); // 输出 false
}
}
此时,由于info是一个变量,变量的内容是可以修改的,因此程序在加载时,并不能确定info是什么内容,所以strB的最终内容也是不能够确定的。
public class StringDemo {
public static void main(String[] args) {
String str = "www.";
str += "mldn.";
str += "cn";
System.out.println(str);
}
}
通过上述的例子可以发现,字符串常量的内容并没有发生任何的改变,改变的只是String类对象的引用,并且这种改变将有空可能带来大量的垃圾空间。
因此,String类在开发中,不要进行内容的频繁修改。
public static void main(String[] args)
public class StringDemo {
public static void main(String[] args) {
for (String arg : args) {
System.out.println(arg);
}
}
}
JavaDoc:Java的API文档。
文档地址(可选择JDK版本):https://docs.oracle.com/en/java/javase/index.html
JDK9(JDK1.9)的文档地址:https://docs.oracle.com/javase/9/docs/api/overview-summary.html
在JDK1.9之前,Java中的常用类库都会在JVM启动时进行全部的加载,这样性能会有所下降,因此在JDK1.9之后,提供了模块化设计,将一些程序类放在了不同的模块(Module)里面。
String类文档的查找(JDK9):
文档结构:
String类的方法:
No | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public String(char[] value) | 构造 | 将字符数组的全部内容转为字符串 |
2 | public String(char[] value, int offset, int count) | 构造 | 将字符数组的部分内容转为字符串 |
3 | public char charAt(int index) | 普通 | 获取指定索引位置的字符 |
4 | public char[] toCharArray() | 普通 | 将字符串转为字符数组 |
public class StringDemo {
public static void main(String[] args) {
String str = "pikaqiu";
char c = str.charAt(5); // 获取指定索引位置的字符(索引下标从0开始)
System.out.println(c); // 输出 i
}
}
public class StringDemo {
public static void main(String[] args) {
String str = "pikaqiu";
char[] arr = str.toCharArray(); // 字符串转字符数组
for (char c : arr) {
System.out.println(c);
}
}
}
No | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public String(byte[] bytes) | 构造 | 将字节数组的全部内容转为字符串 |
2 | public String(byte[] bytes, int offset, int length) | 构造 | 将字节数组的部分内容转为字符串 |
3 | public byte[] getBytes() | 普通 | 将字符串转为字节数组 |
4 | public byte[] getBytes(String charsetName) throws UnsupportedEncodingException | 普通 | 编码转换 |
public class StringDemo {
public static void main(String[] args) {
String str = "pikaqiu";
byte[] arr = str.getBytes(); // 字符串转字节数组
for (byte b : arr) {
System.out.println(c);
}
}
}
No | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public boolean equals(String anObject) | 普通 | 区分大小写的相等判断 |
2 | public boolean equalsIgnoreCase(String anotherString) | 普通 | 不区分大小写的相等判断 |
3 | public int compareTo(String anotherString) | 普通 | 区分大小写地进行字符串大小的比较 |
4 | public int compareToIgnoreCase(String str) | 普通 | 不区分大小写地进行字符串大小的比较 |
public class StringDemo {
public static void main(String[] args) {
String strA = "pikaqiu";
String strB = "PIKAQIU";
System.out.println(strA.equals(strB)); // 输出 false
System.out.println(strA.equalsIgnoreCase(strB)); // 输出 true
}
}
public class StringDemo {
public static void main(String[] args) {
String strA = "A"; // 字符编码为65
String strB = "a"; // 字符编码为97
String strC = "b"; // 字符编码为98
System.out.println(strA.compareTo(strB)); // 输出 -32
System.out.println(strB.compareTo(strA)); // 输出 32
System.out.println(strB.compareTo(strC)); // 输出 -1
System.out.println("————————————————");
System.out.println(strA.compareToIgnoreCase(strB)); // 输出 0
}
}
No | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public boolean contains(String s) | 普通 | 判断子字符串是否存在 |
2 | public int indexOf(String str) | 普通 | 由前向后查找指定字符串的位置(找不到返回-1) |
3 | public int indexOf(String str, int fromIndex) | 普通 | 从指定索引开始查找指定字符串的位置 |
4 | public int lastIndexOf(String str) | 普通 | 由后向前查找指定字符串的位置(找不到返回-1) |
5 | public int lastIndexOf(String str, int fromIndex) | 普通 | 从指定索引由后向前查找指定字符串的位置 |
6 | public boolean startsWith(String prefix) | 普通 | 判断是否以指定的字符串开头 |
7 | public boolean startsWith(String prefix, int toffset) | 普通 | 从指定索引开始判断是否以指定的字符串开头 |
8 | public boolean endsWith(String suffix) | 普通 | 判断是否以指定的字符串结尾 |
判断子字符串是否存在
public class StringDemo {
public static void main(String[] args) {
String str = "pikaqiu";
System.out.println(str.contains("pi")); // 输出 true
System.out.println(str.contains("pin")); // 输出 false
}
}
contains()方法是JDK1.5后追加的,而在JDK1.5之前,往往只能依靠indexOf()来进行数据的查询。
public class StringDemo {
public static void main(String[] args) {
String str = "pikaqiu";
System.out.println(str.indexOf("pi")); // 输出 0
System.out.println(str.indexOf("pin")); // 输出 -1
}
}
No | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public String replaceAll(String regex, String replacement) | 普通 | 替换全部 |
2 | public String replaceFirst(String regex, String replacement) | 普通 | 替换首个 |
public class StringDemo {
public static void main(String[] args) {
String str = "helloworld";
System.out.println(str.replaceAll("l", "X")); // 输出 heXXoworXd
System.out.println(str.replaceFirst("l", "X")); // 输出 heXloworld
}
}
No | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public String[] split(String regex) | 普通 | 按指定字符串全部拆分 |
2 | public String[] split(String regex, int limit) | 普通 | 按指定字符串拆分为指定个数 |
public class StringDemo {
public static void main(String[] args) {
String str = "hello world hello pikaqiu";
String[] arr1 = str.split(" "); // 用空格进行全部拆分
String[] arr2 = str.split(" ", 2); // 用空格拆分成2个
for (String s : arr1) {
System.out.println(s);
}
System.out.println("————————————————");
for (String s : arr2) {
System.out.println(s);
}
}
}
如果遇到拆分不了的情况,则需要使用“\\”进行转义。
public class StringDemo {
public static void main(String[] args) {
String str = "192.168.1.1";
String[] arr = str.split("\\.");
for (String s : arr) {
System.out.println(s);
}
}
}
No | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public String substring(int beginIndex) | 普通 | 从指定索引截取到最后 |
2 | public String substring(int beginIndex, int endIndex) | 普通 | 从指定索引截取到指定索引 |
public class StringDemo {
public static void main(String[] args) {
String str = "pikaqiu";
System.out.println(str.substring(2)); // 输出 kaqiu
System.out.println(str.substring(2, 4)); // 输出 ka
}
}
No | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public static String format(String format, 各种类型 … args) | 普通 | 按指定格式进行字符串的输出 |
public class StringDemo {
public static void main(String[] args) {
String name = "皮卡丘";
int age = 18;
double score = 98.987654321;
String str = String.format("姓名:%s,年龄:%d,成绩:%5.2f", name, age, score);
System.out.println(str); // 输出 姓名:皮卡丘,年龄:18,成绩:98.99
}
}
No | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public String concat(String str) | 普通 | 连接字符串 |
2 | public String intern() | 普通 | 将字符串手动入池 |
3 | public boolean isEmpty() | 普通 | 判断字符串是否为空 |
4 | public int length() | 普通 | 获取字符串的长度 |
5 | public String trim() | 普通 | 去除字符串左右两边的空格 |
6 | public String toUpperCase() | 普通 | 转大写 |
7 | public String toLowerCase() | 普通 | 转小写 |
concat()方法:
public class StringDemo {
public static void main(String[] args) {
String strA = "pikaqiu";
String strB = "pi".concat("ka").concat("qiu");
System.out.println(strB); // 输出 pikaqiu
System.out.println(strA == strB); // 输出 false
}
}
(个人分析:从concat()方法的源代码可以看到,是通过数组拷贝实现的拼接,所以最后返回的是new String的新数组。new是开辟了新的堆空间。)
isEmpty()方法:
public class StringDemo {
public static void main(String[] args) {
String str = null;
System.out.println("".isEmpty()); // 输出 true
System.out.println("pikaqiu".isEmpty()); // 输出 false
System.out.println(str.isEmpty()); // 抛出NullPointerException异常
}
}
在字符串定义时,“""”和“null”不是一个概念,前者表示有实例化对象,后者表示梅伊欧实例化对象,而isEmpty()是判断字符串的内容,所以应该在有实例化对象时进行调用,否则会抛出“java.lang.NullPointerException”的空指针异常。
length() 与 trim() 方法:
public class StringDemo {
public static void main(String[] args) {
String str = " hello pikaqiu ";
System.out.println(str.length()); // 输出 15
str = str.trim(); // 去首尾空格
System.out.println(str.length()); // 输出 13
}
}
toUpperCase() 与 toLowerCase() 方法:
public class StringDemo {
public static void main(String[] args) {
String str = "Hello Pikaqiu";
System.out.println(str.toUpperCase()); // 输出 HELLO PIKAQIU
System.out.println(str.toLowerCase()); // 输出 hello pikaqiu
String str2 = "123456";
System.out.println(str2.toUpperCase()); // 输出 123456
System.out.println(str2.toLowerCase()); // 输出 123456
}
}
自定义首字母大写的方法:
class StringUtil {
public static String initCap(String str) {
// null或空时直接返回
if (null == str || "".equals(str)) return str;
// 字符串长度为1时直接转大写并返回
if (1 == str.length()) return str.toUpperCase();
// 将首字母转大写后进行拼接再返回
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
}
public class StringDemo {
public static void main(String[] args) {
System.out.println(StringUtil.initCap("hello"));
}
}
实现继承的关键字:extends。
实现继承的语法:
class 子类 extends 父类 {}
特别需要注意的是,很多情况下会把子类称为派生类,把父类称为超类。
子类实现继承:
class Person {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return this.age;
}
public String toString() {
return "姓名:" + this.getName() + ",年龄:" + this.getAge();
}
}
// Student是Person的子类,Person是Student的父类
class Student extends Person {
// 在子类中不定义任何功能
}
public class JavaDemo {
public static void main(String[] args) {
Student stu = new Student();
stu.setName("林大强"); // 父类定义
stu.setAge(38); // 父类定义
System.out.println(stu);
}
}
继承实现的主要目的是在于子类可以重用父类中的结构,并且也可以实现功能的扩充,那么同时强调了:子类可以定义更多的内容,并且描述的范围更小。
子类实现扩充:
class Student extends Person {
private String school;
public void setSchool(String school) {
this.school = school;
}
public String getSchool() {
return this.school;
}
public String toString() { // 重写父类方法
return super.toString() + ",学校:" + this.getSchool();
}
}
public class JavaDemo {
public static void main(String[] args) {
Student stu = new Student();
stu.setName("林大强"); // 父类定义
stu.setAge(38); // 父类定义
stu.setSchool("家里蹲大学"); // 子类定义
System.out.println(stu);
}
}
class Person {
public Person() {
System.out.println("【Person父类】一个新的Person父类实例化对象产生了。");
}
}
class Student extends Person {
public Student() {
System.out.println("【Student子类】一个新的Student子类实例化对象产生了。");
}
}
public class JavaDemo {
public static void main(String[] args) {
new Student(); // 实例化子类对象
// 执行结果如下:
// 【Person父类】一个新的Person父类实例化对象产生了。
// 【Student子类】一个新的Student子类实例化对象产生了。
}
}
class Person {
public Person() {
System.out.println("【Person父类】一个新的Person父类实例化对象产生了。");
}
}
class Student extends Person {
public Student() {
super(); // 写不写此语句都一样
System.out.println("【Student子类】一个新的Student子类实例化对象产生了。");
}
}
public class JavaDemo {
public static void main(String[] args) {
new Student(); // 实例化子类对象
// 输出如下:
// 【Person父类】一个新的Person父类实例化对象产生了。
// 【Student子类】一个新的Student子类实例化对象产生了。
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
class Student extends Person {
}
public class JavaDemo {
public static void main(String[] args) {
new Student(); // 实例化子类对象。此时执行,会抛出错误(非异常)。
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
class Student extends Person {
private String school;
public Student(String name, int age, String school) {
super(name, age);
this.school = school;
}
}
public class JavaDemo {
public static void main(String[] args) {
new Student("林小强", 48, "北京大学"); // 实例化子类对象。正常实例化,无错误(非异常)抛出。
}
}
class A {}
class B {}
class C extends A,B {} // 多重继承
class A {}
class B extends A {}
class C extends B {} // 多层继承(最好不要超过三层)
class Person {
public String name = "皮卡"; // name属性为public公共的
}
class Student extends Person {
public Student() {
// 都正常输出 皮卡
System.out.println("this.name = " + this.name);
System.out.println("super.name = " + super.name);
}
}
public class JavaDemo {
public static void main(String[] args) {
new Student();
}
}
class Person {
private String name = "皮卡"; // name属性为private私有的
}
class Student extends Person {
public Student() {
// 直接访问私有属性,会抛出错误(非异常)
System.out.println("this.name = " + this.name);
System.out.println("super.name = " + super.name);
}
}
public class JavaDemo {
public static void main(String[] args) {
new Student();
}
}
class Person {
private String name = "皮卡";
public String getName() {
return name;
}
}
class Student extends Person {
public Student() {
// 间接访问私有属性,正常调用
System.out.println("this.getName() :" + this.getName());
System.out.println("super.getName() :" + super.getName());
}
}
public class JavaDemo {
public static void main(String[] args) {
new Student();
}
}
class Channel {
public void connect() {
System.out.println("【Channel父类】进行资源的连接。");
}
}
class DatabaseChannel extends Channel {
public void connect() { // 进行方法的覆写
System.out.println("【DatabaseChannel子类】进行数据库资源的连接。");
}
}
public class JavaDemo {
public static void main(String[] args) {
DatabaseChannel channel = new DatabaseChannel();
channel.connect(); // 输出 【DatabaseChannel子类】进行数据库资源的连接。
}
}
class Channel {
public void connect() {
System.out.println("【Channel父类】进行资源的连接。");
}
}
class DatabaseChannel extends Channel {
public void connect() { // 进行方法的覆写
super.connect(); // 调用父类的方法
System.out.println("【DatabaseChannel子类】进行数据库资源的连接。");
}
}
public class JavaDemo {
public static void main(String[] args) {
DatabaseChannel channel = new DatabaseChannel();
channel.connect();
// 输出如下:
// 【Channel父类】进行资源的连接。
// 【DatabaseChannel子类】进行数据库资源的连接。
}
}
在子类中,覆写的方法不能拥有比父类方法更严格的访问控制权限。
访问控制权限:public > default(不写) > private 。
class Channel {
public void connect() { // 访问控制权限为public
System.out.println("【Channel父类】进行资源的连接。");
}
}
class DatabaseChannel extends Channel {
void connect() { // 进行方法的覆写。访问控制权限为default,权限比public小,会抛出错误(非异常)
super.connect();
System.out.println("【DatabaseChannel子类】进行数据库资源的连接。");
}
}
class Channel {
public void connect() { // 访问控制权限为public
System.out.println("【Channel父类】进行资源的连接。");
}
}
class DatabaseChannel extends Channel {
void connect() { // 进行方法的覆写。访问控制权限为public,可以正常被调用
super.connect();
System.out.println("【DatabaseChannel子类】进行数据库资源的连接。");
}
}
也就是说,子类中覆写方法的访问控制权限,只能大于或等于父类中被腹泻方法的访问控制权限。
有一种特殊情况,是针对private权限的。在父类中定义private权限的属性或方法时,由于其是私有的,对于子类是不可见的,因此如果父类的方法是private的话,不存在子类覆写该方法。
class Channel {
public void connect() { // 访问控制权限为public
System.out.println("【Channel父类】进行资源的连接。");
}
public void fun() {
this.connect();
}
}
class DatabaseChannel extends Channel {
public void connect() { // 进行方法的覆写
System.out.println("【DatabaseChannel子类】进行数据库资源的连接。");
}
}
public class JavaDemo {
public static void main(String[] args) {
DatabaseChannel channel = new DatabaseChannel();
channel.fun(); // 输出 【DatabaseChannel子类】进行数据库资源的连接。
// 调用父类中的fun()时,由于子类覆写了connect(),所以这里调用的是子类的connect()
}
}
class Channel {
private void connect() { // 访问控制权限为private
System.out.println("【Channel父类】进行资源的连接。");
}
public void fun() {
this.connect();
}
}
class DatabaseChannel extends Channel {
public void connect() { // 进行方法的覆写
System.out.println("【DatabaseChannel子类】进行数据库资源的连接。");
}
}
public class JavaDemo {
public static void main(String[] args) {
DatabaseChannel channel = new DatabaseChannel();
channel.fun(); // 输出 【DatabaseChannel子类】进行数据库资源的连接。
// 调用父类中的fun()时,由于父类中的connect()为private,不存在方法的覆写,所以这里调用的是父类的connect()
}
}
// 总结说明
public void fun() {
// 如存在方法覆写,则调用子类的connect()
// 如不存在方法覆写,则调用父类的connect()
this.connect();
}
个人总结:
当存在继承关系,实例化的是子类对象时:
1. 如调用了子类中未覆写的方法,则会往上查找,实际调用父类的方法。
2. 如在父类方法中使用this关键字调用方法A,则会往下查找,判断子类中是否有对方法A进行覆写,有则调用子类方法,无则调用父类方法。
NO | 区别 | Override | Overloading |
---|---|---|---|
1 | 中文含义 | 重载 | 重写(覆写) |
2 | 概念 | 方法名称相同,参数的类型及个数不同 | 方法名称、参数类型及个数、返回值相同 |
3 | 权限 | 没有权限限制 | 被重写方法不能拥有更严格的控制权限 |
4 | 范围 | 发生在一个类中 | 发生在继承关系中 |
class Channel {
String info = "pikaqiu";
}
class DatabaseChannel extends Channel {
String info = "hello pikaqiu"; // 属性覆盖
public void fun() {
System.out.println("super.info = " + super.info); // 输出 super.info = pikaqiu
System.out.println("this.info = " + this.info); // 输出 this.info = hello pikaqiu
}
}
public class JavaDemo {
public static void main(String[] args) {
DatabaseChannel channel = new DatabaseChannel();
channel.fun();
}
}
final在程序之中描述的是一种终结器的概念。
在java里,使用final关键字可以实现如下的功能:
使用 final 定义不能被继承的类:
final class Channel {} // 使用了final关键字定义类,则Channel类不可以被继承
class DatabaseChannel extends Channel {} // 想要继承Channel类,会抛出错误(非异常)
public class JavaDemo {
public static void main(String[] args) {}
}
class Channel {
public final void connect() {} // 使用了final关键字定义方法,则connect()方法不可以被覆写
}
class DatabaseChannel extends Channel {
public void connect() {} // 想要覆写connect()方法,会抛出错误(非异常)
}
public class JavaDemo {
public static void main(String[] args) {}
}
class Channel {
private final int OFF = 0; // 使用了final关键字定义属性,则该属性不可被修改
private final int ON = 1; // 使用了final关键字定义属性,则该属性不可被修改
public final void connect() {
ON = 2; // 想要修改ON属性,会抛出错误(非异常)
}
}
public class JavaDemo {
public static void main(String[] args) {}
}
public class StringDemo {
public static void main(String[] args) {
final String INFO = "ka"; // 使用final关键字定义属性,则info为常量
String strA = "pikaqiu";
String strB = "pi" + INFO + "qiu";
System.out.println(strA == strB); // 输出 true
}
}
// 可与课时45中的代码示例进行对比
class Person {
private String name;
private char sex;
private int age;
private String addr;
public Person() {}
public Person(String name, char sex) {
this(name, sex, 0, "");
}
public Person(String name, char sex, int age, String addr) {
this.name = name;
this.sex = sex;
this.age = age;
this.addr = addr;
}
public String getInfo() {
return "姓名:" + this.name +
",性别:" + this.sex +
",年龄:" + this.age +
",地址:" + this.addr;
}
}
class Student extends Person {
private double math;
private double english;
public Student() {}
public Student(double math, double english) {
this.math = math;
this.english = english;
}
public Student(String name, char sex, int age, String addr, double math, double english) {
super(name, sex, age, addr);
this.math = math;
this.english = english;
}
public String getInfo() {
return super.getInfo() + ",数学成绩:" + this.math + ",英语成绩:" + this.english;
}
}
public class JavaDemo {
public static void main(String[] args) {
Student stu = new Student("张三", '男', 18, "天安门", 78.99, 89.98);
System.out.println(stu.getInfo());
}
}
class Employee {
private String name;
private int age;
private char sex;
public Employee() {}
public Employee(String name, int age, char sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
public String getInfo() {
return "姓名:" + this.name + ",年龄:" + this.age + ",性别:" + this.sex;
}
}
class Manager extends Employee {
private String job;
private double income;
public Manager() {}
public Manager(String name, int age, char sex, String job, double income) {
super(name, age, sex);
this.job = job;
this.income = income;
}
public String getInfo() {
return "【管理层】" + super.getInfo() + ",职务:" + this.job + ",年薪:" + this.income;
}
}
class Staff extends Employee {
private String dept;
private double salary;
public Staff() {}
public Staff(String name, int age, char sex, String dept, double salary) {
super(name, age, sex);
this.dept = dept;
this.salary = salary;
}
public String getInfo() {
return "【职员】" + super.getInfo() + ",部门:" + this.dept + ",月薪:" + this.salary;
}
}
public class JavaDemo {
public static void main(String[] args) {
Manager man = new Manager("张三", 28, '女', "主管", 150000.00);
Staff sta = new Staff("李四", 18, '男', "出纳", 3000.00);
System.out.println(man.getInfo());
System.out.println(sta.getInfo());
}
}
class StringUtil {
// 返回的整型数组,第一个内容为字母n的个数,第二个内容为字母o的个数
public static int[] count(String str) {
int[] countData = new int[2];
char[] data = str.toCharArray(); // 将字符串转为字符数组
for (int i = 0; i < data.length; i ++) {
switch (data[i]) {
case 'n':
case 'N':
countData[0] ++;
break;
case 'o':
case 'O':
countData[1] ++;
break;
}
}
return countData;
}
}
public class JavaDemo {
public static void main(String[] args) {
String str = "want you to know one thing";
int[] result = StringUtil.count(str);
System.out.println("字母n的个数:" + result[0]);
System.out.println("字母o的个数:" + result[1]);
}
}
class StringUtil {
private String content;
public StringUtil(String content) {
this.content = content;
}
public String getContent() {
return this.content;
}
}
class StringCount extends StringUtil {
private int nCount;
private int oCount;
public StringCount(String content) {
super(content);
this.countChar();
}
public void countChar() {
char[] data = super.getContent().toCharArray(); // 将字符串转为字符数组
for (int i = 0; i < data.length; i ++) {
switch (data[i]) {
case 'n':
case 'N':
nCount ++;
break;
case 'o':
case 'O':
oCount ++;
break;
}
}
}
public int getNCount() {
return nCount;
}
public int getOCount() {
return oCount;
}
public String getInfo() {
return "字母n的个数:" + this.nCount + ",字母o的个数:" + this.oCount;
}
}
public class JavaDemo {
public static void main(String[] args) {
StringCount sc = new StringCount("want you to know one thing");
System.out.println(sc.getInfo());
}
}
可以实现整型数组的操作类(Array)
操作类(Array)的子类:
实现步骤:
第一步:实现基本的数组操作类的定义:
class Array { // 数组的操作类
private int foot; // 进行数组的脚标控制
private int[] data; // 整型数组
public Array(int len) {
if (0 < len) this.data = new int[len]; // 开辟指定大小的数组空间
else this.data = new int[1]; // 默认开辟一个长度的数组空间
}
public int[] getData() {
return this.data;
}
// 增加数据
public boolean add(int num) {
if (this.data.length > this.foot) { // 有空位置
this.data[this.foot ++] = num;
return true;
}
return false;
}
// 扩充数组(扩充num个长度)
public void increment(int num) {
int[] newData = new int [this.data.length + num]; // 创建新数组
System.arraycopy(this.data, 0, newData, 0, this.data.length); // 拷贝数组
this.data = newData; // 赋值新数组给data
}
}
public class JavaDemo {
public static void main(String[] args) {
Array arr = new Array(5);
System.out.println(arr.add(10));
System.out.println(arr.add(5));
System.out.println(arr.add(20));
System.out.println(arr.add(3));
System.out.println(arr.add(6));
arr.increment(3);
System.out.println(arr.add(1));
System.out.println(arr.add(7));
System.out.println(arr.add(0));
}
}
第二步:实现排序子类的定义:
class SortArray extends Array { // 数组操作的排序子类
public SortArray(int len) {
super(len);
}
public int[] getData() {
java.util.Arrays.sort(super.getData()); // 排序
return super.getData();
}
}
public class JavaDemo {
public static void main(String[] args) {
SortArray arr = new SortArray(5);
arr.add(10);
arr.add(5);
arr.add(20);
arr.add(3);
arr.add(6);
int[] result = arr.getData();
for (int temp : result) {
System.out.print(temp + " ");
}
// 最终输出:3 5 6 10 20
}
}
第三步:实现反转子类的定义:
class ReverseArray extends Array { // 数组操作的反转子类
public ReverseArray(int len) {
super(len);
}
public int[] getData() {
int center = super.getData().length / 2;
int head = 0;
int tail = super.getData().length - 1;
for (int i = 0; i < center; i++) {
int temp = super.getData()[head];
super.getData()[head] = super.getData()[tail];
super.getData()[tail] = temp;
head ++;
tail --;
}
return super.getData();
}
}
public class JavaDemo {
public static void main(String[] args) {
ReverseArray arr = new ReverseArray(5);
arr.add(10);
arr.add(5);
arr.add(20);
arr.add(3);
arr.add(6);
int[] result = arr.getData();
for (int temp : result) {
System.out.print(temp + " ");
}
// 最终输出:6 3 20 5 10
}
}
Annotation时从JDK1.5之后提出的一个新的开发技术结构,利用Annotation可以有效地减少程序配置的代码,并且可以进行一些结构化的定义。
Annotation是以一种注解的形式实现的程序开发。
程序开发结构的历史:
Java提供的基本注解:
准确覆写:@Override
当子类继承一个父类之后,如果发现父类中的某些方法功能不足,往往会采用覆写的形式来对方法功能进行扩充。
实现继承时经常出现的两个问题:
问题一:在定义子类时,忘记extends父类,那这样就不是覆写了。
class Channel {
public void connect() {
System.out.println("———————————— 通道的连接 ————————————");
}
}
class DatabaseChannel {
public void connect() {
System.out.println("———————————— 数据库通道的连接 ————————————");
}
}
public class JavaDemo {
public static void main(String[] args) {
new DatabaseChannel().connect();
// 输出:———————————— 数据库通道的连接 ————————————
}
}
问题二:在子类中覆写方法时,方法名称写错了。
class Channel {
public void connect() {
System.out.println("———————————— 通道的连接 ————————————");
}
}
class DatabaseChannel extends Channel {
public void connection() {
System.out.println("———————————— 数据库通道的连接 ————————————");
}
}
public class JavaDemo {
public static void main(String[] args) {
new DatabaseChannel().connect();
// 输出:———————————— 通道的连接 ————————————
}
}
所以为了避免上述两个问题,则可以在明确覆写的方法上增加“@Override”注解,这样在编译时才会产生错误信息。
class Channel {
public void connect() {
System.out.println("———————————— 通道的连接 ————————————");
}
}
class DatabaseChannel extends Channel {
@Override
public void connection() {
System.out.println("———————————— 数据库通道的连接 ————————————");
}
}
public class JavaDemo {
public static void main(String[] args) {
new DatabaseChannel().connect(); // 抛出错误(非异常)信息
}
}
该注解主要是帮助开发者在程序编译时就可以检查出程序的错误。
过期操作:@Deprecated
过期操作:指的是在一个软件项目的迭代开发过程之中,可能有某个方法或类,由于在最初设计时考虑不周(存在缺陷),导致新版本的应用会有不适应的地方(老版本不受影响),这时候又不可能直接删除掉这些操作,那么就给一个过度的时间,此时就可以采用过期的声明。目的在于告诉新的用户,这些操作不要再用了。
class Channel {
@Deprecated // 老系统可以继续用,新系统就不要再用了
public void connect() {
System.out.println("———————————— 通道的连接 ————————————");
}
public String connection() {
return "获取了通道连接信息。";
}
}
public class JavaDemo {
public static void main(String[] args) {
new Channel().connect(); // 编译时出现错误提示信息,而运行时可以正常调用
// 输出:———————————— 通道的连接 ————————————
}
}
压制警告:@SuppressWarings
如不想在编译时出现错误的提示信息(如由于使用@Deprecated方法而导致的“使用或覆盖了已过时的API。”),或已经明确知道错误在哪里,那么就可以进行警告信息的压制。
class Channel {
@Deprecated // 老系统可以继续用,新系统就不要再用了
public void connect() {
System.out.println("———————————— 通道的连接 ————————————");
}
public String connection() {
return "获取了通道连接信息。";
}
}
public class JavaDemo {
@SuppressWarnings("all") // 参数为警告类型,“all”表示压制所有警告
public static void main(String[] args) {
new Channel().connect(); // 编译时不再出现错误提示信息
// 输出:———————————— 通道的连接 ————————————
}
}
多态性:面向对象的第三大主要特征,是在继承性的基础上扩展出来的概念,可以实现父子类之间的转换处理。
Java中实现多态性的模式:
方法的多态性:
方法的重载:同一个方法名称,可以根据传入参数的类型或个数的不同,来实现不同的功能;
class Message {
public void print() {
System.out.println("pikaqiu");
}
public void print(String str) {
System.out.println(str);
}
}
方法的重写(覆写):
class Message {
public void print() {
System.out.println("pikaqiu");
}
}
class DatabaseMessage extends Message {
public void print() {
System.out.println("pikaqiu's database");
}
}
对象的多态性:父子实例间的转换处理。
class Message {
public void print() {
System.out.println("pikaqiu");
}
}
class DatabaseMessage extends Message {
public void print() {
System.out.println("pikaqiu's database");
}
}
class ServerMessage extends Message {
public void print() {
System.out.println("pikaqiu's server");
}
}
public class JavaDemo {
public static void main(String[] args) {
fun(new DatabaseMessage()); // Message message = new DatabaseMessage();
fun(new ServerMessage()); // Message message = new ServerMessage();
}
public static void fun(Message message) {
message.print();
}
}
class Person {
public void print() {
System.out.println("一个正常的人类行为:吃饭、睡觉。");
}
}
class SuperMan extends Person {
public void fly() {
System.out.println("我可以飞~");
}
public void fire() {
System.out.println("我可以喷火~");
}
}
public class JavaDemo {
public static void main(String[] args) {
System.out.println("———————————— 正常状态下的超人应该是一个普通人 ————————————");
Person per = new SuperMan(); // 向上转型
per.print();
System.out.println("———————————— 外星怪兽狗骚扰地球,准备消灭人类 ————————————");
SuperMan man = (SuperMan) per;
man.fly();
man.fire();
}
}
public class JavaDemo {
public static void main(String[] args) {
Person per = new Person();
SuperMan man = (SuperMan) per;
man.fly();
man.fire();
}
由于向下转型是一个存在有安全隐患的操作,因此为了保证向下转型的正确性,往往需要在向下转型之前进行判断,判断某个实例是否为某个类的对象,这就需要通过instanceof关键字来实现。
instanceof关键字的使用语法:对象 instanceof 类
instanceof判断的返回值类型:boolean型。
public class JavaDemo {
public static void main(String[] args) {
Person per1 = new Person(); // 不转型
System.out.println(per1 instanceof Person); // 输出:true
System.out.println(per1 instanceof SuperMan); // 输出:false
System.out.println("—————— 分界线 ——————");
Person per2 = new SuperMan(); // 向上转型
System.out.println(per2 instanceof Person); // 输出:true
System.out.println(per2 instanceof SuperMan); // 输出:true
}
}
public class JavaDemo {
public static void main(String[] args) {
Person per = new Person();
if (per instanceof SuperMan) {
SuperMan man = (SuperMan) per;
man.fly();
man.fire();
}
}
}
Object类的主要特点:可以解决参数的统一问题。(Object类可以接收所有的数据类型。)
在Java中,只有一个类是不存在父类的,这个类就是Object类。
所有的类在默认情况下,都是Object的子类,所以可以使用Object来接收所有的子类。
class Person {}
public class JavaDemo {
public static void main(String[] args) {
Object obj = new Person(); // 向上转型
if (obj instanceof Person) {
Person per = (Person) obj;
System.out.println("Person对象向下转型执行完毕。");
}
}
}
获取对象信息:toString()
在Object类中提供有 “toString()” 的方法,该方法可以获取一个对象的完整信息:public String toString()
class Person {}
public class JavaDemo {
public static void main(String[] args) {
Person per = new Person();
System.out.println(per);
System.out.println(per.toString());
}
}
class Person {
@Override
public String toString() {
return "我是一个人";
}
}
public class JavaDemo {
public static void main(String[] args) {
Person per = new Person();
System.out.println(per); // 输出:我是一个人
}
}
对象比较:equals()
对象比较的主要功能:比较两个对象的内容是否完全相同。
在Object类中提供有 “equals()” 的方法:public boolean equals(Object obj) ,默认情况下该方法只进行了两个对象的地址判断。
public boolean equals(Object obj) {
return (this == obj);
}
案例:
传统比较:
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "姓名:" + this.name + ",年龄:" + this.age;
}
}
public class JavaDemo {
public static void main(String[] args) {
Person perA = new Person("张三", 18);
Person perB = new Person("张三", 18);
if (perA.getName().equals(perB.getName())
&& perA.getAge() == perB.getAge()) {
System.out.println("是同一个人");
}
else System.out.println("不是同一个人");
}
}
(对象比较应该是类内部具备的功能,不应该在类外部进行定义。)
覆写equals()方法进行比较:
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public boolean equals(Object obj) {
if (null != obj) return false; // 如果为null,则不需要进行比较
if (this == obj) return true; // 如果内存地址相同,则为同一个对象
if (!(obj instanceof Person)) return false; // 判断是否可以向下转型
Person per = (Person) obj;
return this.getName() == per.getName() && this.getAge() == per.getAge();
}
@Override
public String toString() {
return "姓名:" + this.name + ",年龄:" + this.age;
}
}
public class JavaDemo {
public static void main(String[] args) {
Person perA = new Person("张三", 18);
Person perB = new Person("张三", 18);
System.out.println(perA.equals(perB));
}
}
String类作为Object的子类,实际上已经覆写了equals()方法
在类的继承操作中,对于子类是否需要覆写某个方法,父类无法做出强制性约定。而实际开发中,很少会出现继承一个已经很完善的类。
在以后进行父类设计时,应该优先考虑抽象类。
抽象类的主要作用:可以约定必须在子类中需要进行覆写的方法,也就是可以在抽象类里定义抽象方法。
抽象方法:使用abstract关键字定义的,并且不提供方法体的方法。其所在的类必须为抽象类。
abstract class Message { // 抽象类
private String type; // 消息类型
public abstract String getConnectInfo(); // 抽象方法
public String getType() { // 普通方法
return type;
}
public void setType(String type) { // 普通方法
this.type = type;
}
}
public class JavaDemo {
public static void main(String[] args) {
Message msg = new Message(); // 会抛出错误(非异常),抽象类无法实例化
System.out.println(msg.getConnectInfo());
}
}
abstract class Message { // 抽象类
private String type; // 消息类型
public abstract String getConnectInfo(); // 抽象方法
public String getType() { // 普通方法
return type;
}
public void setType(String type) { // 普通方法
this.type = type;
}
}
class DatabaseMessage extends Message {} // 会抛出错误(非异常),继承了抽象类,但没有覆写抽象类中的抽象方法
public class JavaDemo {
public static void main(String[] args) {}
}
abstract class Message { // 抽象类
private String type; // 消息类型
public abstract String getConnectInfo(); // 抽象方法
public String getType() { // 普通方法
return type;
}
public void setType(String type) { // 普通方法
this.type = type;
}
}
class DatabaseMessage extends Message {
@Override
public String getConnectInfo() {
return "我是数据库消息类";
}
}
public class JavaDemo {
public static void main(String[] args) {
DatabaseMessage msg = new DatabaseMessage();
System.out.println(msg.getConnectInfo()); // 调用覆写的抽象方法,正常输出
msg.setType("数据库消息");
System.out.println(msg.getType()); // 调用普通方法,正常输出
}
}
抽象类只是比普通类多了抽象方法以及对子类强制性覆写的要求,其它完全相同。
使用抽象类的意见及建议:
使用抽象类的注意事项:
在定义抽象类时,不可以使用final关键字,因为抽象类必须要有子类,而final定义的类是不能够有子类的。
抽象类中允许没有抽象方法。
抽象类中可以提供static方法,并且该方法不受抽象类对象的局限。
abstract class Message {
public static Message getInstance() {
return new DatabaseMessage();
}
public abstract String getInfo();
}
class DatabaseMessage extends Message {
@Override
public String getInfo() {
return "数据库连接信息";
}
}
public class JavaDemo {
public static void main(String[] args) {
Message msg = Message.getInstance();
System.out.println(msg.getInfo());
}
}
▲ static方法永远不受实例化对象或结构䣌限制,永远可以直接通过类名称进行调用。
abstract class Action {
public static final int EAT = 1;
public static final int SLEEP = 5;
public static final int WORK = 10;
public void command(int code) {
switch (code) {
case EAT:
this.eat();
break;
case SLEEP:
this.sleep();
break;
case WORK:
this.work();
break;
}
}
public abstract void eat();
public abstract void sleep();
public abstract void work();
}
class Robot extends Action {
@Override
public void eat() {
System.out.println("机器人需要接通电源充电。");
}
@Override
public void sleep() {}
@Override
public void work() {
System.out.println("机器人按照固定的套路进行工作。");
}
}
class Person extends Action {
@Override
public void eat() {
System.out.println("饿的时候坐下,安静地吃饭。");
}
@Override
public void sleep() {
System.out.println("安静地躺下,慢慢地睡着。");
}
@Override
public void work() {
System.out.println("人类是高级动物,所以要有想法地工作。");
}
}
class Pig extends Action {
@Override
public void eat() {
System.out.println("吃食槽中人类的剩饭。");
}
@Override
public void sleep() {
System.out.println("倒地就睡。");
}
@Override
public void work() {}
}
public class JavaDemo {
public static void main(String[] args) {
System.out.println("—————— 机器人行为 ——————");
Action robotAction = new Robot();
robotAction.command(Action.EAT);
robotAction.command(Action.SLEEP);
robotAction.command(Action.WORK);
System.out.println("—————— 人类行为 ——————");
Action personAction = new Person();
personAction.command(Action.EAT);
personAction.command(Action.SLEEP);
personAction.command(Action.WORK);
System.out.println("—————— 猪的行为 ——————");
Action pigAction = new Pig();
pigAction.command(Action.EAT);
pigAction.command(Action.SLEEP);
pigAction.command(Action.WORK);
}
}
抽象类的目的:对所有行为规范进行统一处理(提供模板)。
抽象类最大的好处:
包装类的主要功能是针对基本数据类型的对象转换而实现的,并且随着JDK版本的更新,包装类的功能也在发生着改变,有着更多的支持。
Object类最大的特点是所有类的父类,可以接收所有的数据类型,但基本数据类型都不是类,所以如果想要将基本数据类型以类的形式进行处理的话,就需要对其进行包装。
class Int {
private int data;
public Int(int data) {
this.data = data;
}
public int intValue() {
return this.data;
}
}
public class JavaDemo {
public static void main(String[] args) {
Int temp = new Int(10); // 装箱:将基本数据类型保存在包装类对象中
int x = temp.intValue(); // 拆箱:从包装类对象中获取基本数据类型
System.out.println(x);
}
}
包装类的类型:
Numer类中提供有直接获取包装类中基本数据类型的方法,一共只定义了六个方法:
No | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public byte byteValue() | 普通 | 从包装类中获取byte数据 |
2 | public short shortValue() | 普通 | 从包装类中获取short数据 |
3 | public abstract int intValue() | 普通 | 从包装类中获取int数据 |
4 | public abstract long longValue() | 普通 | 从包装类中获取long数据 |
5 | public abstract float floatValue() | 普通 | 从包装类中获取float数据 |
6 | public abstract double doubleValue() | 普通 | 从包装类中获取double数据 |
基本数据类型的包装类,都是为了基本数据类型转为对象而提供的。
基本数据类型与包装类的操作:
public class JavaDemo {
public static void main(String[] args) {
Integer obj = new Integer(10); // 装箱
int num = obj.intValue(); // 拆箱
System.out.println(num);
}
}
public class JavaDemo {
public static void main(String[] args) {
Double obj = new Double(10.1); // 装箱
double num = obj.doubleValue(); // 拆箱
System.out.println(num);
}
}
public class JavaDemo {
public static void main(String[] args) {
Boolean obj = new Boolean(true); // 装箱
boolean num = obj.booleanValue(); // 拆箱
System.out.println(num);
}
}
public class JavaDemo {
public static void main(String[] args) {
Integer obj = 10; // 自动装箱
int num = obj; // 自动拆箱
obj ++; // 包装类对象可以直接参与数学运算
System.out.println(num); // 输出:10
System.out.println(obj); // 输出:11
}
}
public class JavaDemo {
public static void main(String[] args) {
Object obj = 19.2; // double自动装箱为包装类,向上转型为Object
double num = (Double) obj; // 向下转型为包装类,再自动拆箱
System.out.println(num); // 输出:19.2
}
}
public class JavaDemo {
public static void main(String[] args) {
Integer x = 127;
Integer y = 127;
System.out.println(x == y); // 输出:true
}
}
public class JavaDemo {
public static void main(String[] args) {
Integer x = 128;
Integer y = 128;
System.out.println(x == y); // 输出:false
}
}
public class JavaDemo {
public static void main(String[] args) {
Integer x = -128;
Integer y = -128;
System.out.println(x == y); // 输出:true
}
}
public class JavaDemo {
public static void main(String[] args) {
Integer x = -129;
Integer y = -129;
System.out.println(x == y); // 输出:false
}
}
-128~127为byte的范围,超过byte就不止占1位了。
public class JavaDemo {
public static void main(String[] args) {
Integer x = -129;
Integer y = -129;
System.out.println(x == y); // 输出:false
System.out.println(x.equals(y)); // 输出:true
}
}
public class JavaDemo {
public static void main(String[] args) {
Integer x = 127;
Integer y = new Integer(127); // 使用了new关键字,开辟新的堆空间
System.out.println(x == y); // 输出:false
System.out.println(x.equals(y)); // 输出:true
}
}
当你可以灵活地使用抽象类和接口进行设计时,那么基本上就表示你对面向对象的概念理解了。这一步是需要大量的程序代码累积而成的。
接口的基本定义:
// 由于类名称与接口名称的定义要求相同,所以为了区分出接口,接口名称前往往会加字母I
interface IMessage { // 定义了一个接口
public static final String INFO = "pikaqiu"; // 全局常量
public abstract String getInfo(); // 抽象方法
}
// 由于类名称与接口名称的定义要求相同,所以为了区分出接口,接口名称前往往会加字母I
interface IMessage { // 定义了一个接口
public static final String INFO = "pikaqiu"; // 全局常量
public abstract String getInfo(); // 抽象方法
}
class MessageImpl implements IMessage { // 实现了接口
@Override
public String getInfo() {
return "得到一个消息";
}
}
public class JavaDemo {
public static void main(String[] args) {
IMessage msg = new MessageImpl();
System.out.println(msg.getInfo()); // 输出覆写方法
System.out.println(msg.INFO); // 输出全局常量
}
}
// 由于类名称与接口名称的定义要求相同,所以为了区分出接口,接口名称前往往会加字母I
interface IMessage { // 定义了一个接口
public static final String INFO = "pikaqiu"; // 全局常量
public abstract String getInfo(); // 抽象方法
}
interface IChannel {
public abstract boolean connect(); // 抽象方法
}
class MessageImpl implements IMessage,IChannel { // 实现了接口
@Override
public String getInfo() {
if (this.connect()) return "得到一个消息。";
else return "通道创建失败,无法获取消息。";
}
@Override
public boolean connect() {
System.out.println("消息发送通道已经成功建立。");
return true;
}
}
public class JavaDemo {
public static void main(String[] args) {
IMessage msg = new MessageImpl();
System.out.println(msg.getInfo());
System.out.println(msg.INFO);
}
}
由于接口描述的是一个公共的定义标准,所以在接口中所有抽象方法的访问权限都为public,也就是说写不写public都是一样的。
完整定义:
interface IMessage { // 定义了一个接口
public static final String INFO = "pikaqiu"; // 全局常量
public abstract String getInfo(); // 抽象方法
}
简化定义:
interface IMessage { // 定义了一个接口
String INFO = "pikaqiu"; // 全局常量
String getInfo(); // 抽象方法
}
一个类可以实现多个接口,但只能够继承一个类,要求先继承后实现。
接口多继承:一个接口可以通过extends关键字继承若干个父接口,
interface IMessage {
public abstract String getInfo();
}
interface IChannel {
public boolean connect();
}
// extends在类继承上只能够继承一个父类,但接口上可以继承多个
interface IService extends IMessage,IChannel { // 接口多继承
public String service();
}
class MessageService implements IService {
public String getInfo() {}
public boolean connect() {
return true;
}
public String service() {
return "获取消息的服务";
}
}
interface IMessage {
String getInfo();
default boolean connect() { // 公共方法
System.out.println("建立消息的发送通道。");
return true;
}
}
class MessageImpl implements IMessage {
@Override
public String getInfo() {
return "pikaqiu";
}
}
public class JavaDemo {
public static void main(String[] args) {
IMessage msg = new MessageImpl();
if (msg.connect()) msg.getInfo();
}
}
接口中的普通方法必须使用default的声明,并且需要注意的是,该操作属于挽救措施,所以在非必须的情况下,不应该作为设计的首选。
在接口中定义静态方法:
interface IMessage {
String getInfo();
default boolean connect() { // 公共方法
System.out.println("建立消息的发送通道。");
return true;
}
static IMessage getInstance() { // 静态方法
return new MessageImpl(); // 获得子类对象
}
}
class MessageImpl implements IMessage {
@Override
public String getInfo() {
return "pikaqiu";
}
}
public class JavaDemo {
public static void main(String[] args) {
IMessage msg = IMessage.getInstance(); // 调用接口的静态方法
if (msg.connect()) msg.getInfo();
}
}
interface IUSB { // 定义USB标准
boolean check();
void work();
}
class Computer {
public void plugin(IUSB usb) {
if (usb.check()) usb.work();
}
}
class Keyboard implements IUSB {
@Override
public boolean check() {
return true;
}
@Override
public void work() {
System.out.println("开始进行码字任务。");
}
}
class Print implements IUSB {
@Override
public boolean check() {
return false;
}
@Override
public void work() {
System.out.println("开始进行照片打印。");
}
}
public class JavaDemo {
public static void main(String[] args) {
Computer computer = new Computer();
computer.plugin(new Keyboard()); // 插入键盘
computer.plugin(new Print()); // 插入打印机
}
}
对象实例化中存在的设计问题:
想吃面包:
interface IFood { // 定义一个食物标准
void eat(); // 吃
}
class Bread implements IFood { // 定义面包类
@Override
public void eat() {
System.out.println("吃面包。");
}
}
public class JavaDemo {
public static void main(String[] args) {
IFood food = new Bread();
food.eat(); // 吃面包
}
}
不想吃面包了,想喝牛奶:
interface IFood { // 定义一个食物标准
void eat(); // 吃
}
class Bread implements IFood { // 定义面包类
@Override
public void eat() {
System.out.println("吃面包。");
}
}
class Milk implements IFood { // 定义面包类
@Override
public void eat() {
System.out.println("喝牛奶。");
}
}
public class JavaDemo {
public static void main(String[] args) {
IFood food = new Milk();
food.eat(); // 喝牛奶
}
}
上述两个程序表出现了耦合的问题,而造成耦合最直接的元凶:new关键字。
而以JVM的设计为例,Java能实现可移植性的关键在于JVM,而JVM的核心原理:利用一个虚拟机来运行Java程序,所有的程序并不与具体的操作系统有任何的关联,而是由JVM来进行匹配。
所以得出结论:良好的设计应该避免耦合。
工厂设计模式:
改进代码:
import java.util.Scanner;
interface IFood { // 定义一个食物标准
void eat(); // 吃
}
class Bread implements IFood { // 定义面包类
@Override
public void eat() {
System.out.println("吃面包。");
}
}
class Milk implements IFood { // 定义面包类
@Override
public void eat() {
System.out.println("喝牛奶。");
}
}
class Factory {
public static IFood getInstance(String className) {
if ("bread".equals(className)) {
return new Bread();
}
else if ("milk".equals(className)) {
return new Bread();
}
else return null;
}
}
public class JavaDemo {
public static void main(String[] args) {
IFood food = Factory.getInstance(args[0]);
food.eat();
}
}
此时,客户端程序类与IFood接口的子类没有任何的关联,所有的关联都是通过Factory类来完成的,而在程序运行的时候,可以通过初始化参数来进行要使用子类的定义:
java JavaDemo bread
java JavaDemo mild
interface IEat {
public void get();
}
class EatReal implements IEat {
@Override
public void get() {
System.out.println("【真实主题】获得一份食物。");
}
}
class EatProxy implements IEat { // 服务代理
private IEat eat; // 为吃而服务
public EatProxy(IEat eat) { // 代理项
this.eat = eat;
}
@Override
public void get() {
this.prepare();
this.eat.get();
this.clean();
}
public void prepare() {
System.out.println("【代理主题】1、精心购买食材。");
System.out.println("【代理主题】2、小心处理食材。");
}
public void clean() {
System.out.println("【代理主题】3、收拾碗筷。");
}
}
public class JavaDemo {
public static void main(String[] args) {
IEat eat = new EatProxy(new EatReal());
eat.get();
}
}
NO | 区别 | 抽象类 | 接口 |
---|---|---|---|
1 | 定义关键字 | abstract class 抽象类名称 {} | interface 接口名称 {} |
2 | 组成 | 构造、普通方法、静态方法、全局常量、普通成员 | 抽象方法、全局常量、普通方法、静态方法 |
3 | 权限 | 各种权限 | 只能使用public |
4 | 子类使用 | 子类通过extends关键字只能继承一个抽象类 | 子类通过implements关键字可以实现多个接口 |
5 | 两者关系 | 可以实现若干个接口 | 接口不允许继承抽象类,但允许继承多个父接口 |
抽象类和接口的使用:
当抽象类和接口都可以使用的情况下,要优先考虑接口,因为接口可以避免子类的单继承局限。
抽象类与接口是Java里最为核心的概念,也是所有设计模式的综合体现。
案例:
interface IClassName {
public String getClassName();
}
class Company implements IClassName {
@Override
public String getClassName() {
return "Company";
}
}
public class JavaDemo {
public static void main(String[] args) {
IClassName ica = new Company();
System.out.println(ica.getClassName());
}
}
interface IGraphical { // 定义绘图标准
public void paint(); // 绘图
}
class Point {
private double x;
private double y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
public double getX() {
return this.x;
}
public double getY() {
return this.y;
}
}
class Triangle implements IGraphical {
private Point[] x; // 保存第一条边的坐标
private Point[] y; // 保存第二条边的坐标
private Point[] z; // 保存第三条边的坐标
public Triangle(Point[] x, Point[] y, Point[] z) {
this.x = x;
this.y = y;
this.z = z;
}
@Override
public void paint() {
System.out.println("绘制第一条边,开始坐标[" + this.x[0].getX() + ", " + this.x[0].getY() +"]," +
"结束坐标[" + this.x[1].getX() + "," + this.x[1].getY() +"]");
System.out.println("绘制第二条边,开始坐标[" + this.y[0].getX() + ", " + this.y[0].getY() +"]," +
"结束坐标[" + this.y[1].getX() + "," + this.y[1].getY() +"]");
System.out.println("绘制第三条边,开始坐标[" + this.z[0].getX() + ", " + this.z[0].getY() +"]," +
"结束坐标[" + this.z[1].getX() + "," + this.z[1].getY() +"]");
}
}
class Circle implements IGraphical {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public void paint() {
System.out.println("以半径为" + this.radius + "绘制圆形。");
}
}
class Factory {
public static IGraphical getInstance(String className, double ... args) {
if ("triangle".equalsIgnoreCase(className)) {
return new Triangle(
new Point[] {
new Point(args[0], args[1]), new Point(args[2], args[3]),
},
new Point[] {
new Point(args[4], args[5]), new Point(args[6], args[7]),
},
new Point[] {
new Point(args[8], args[9]), new Point(args[10], args[11]),
}
);
}
else if ("circle".equalsIgnoreCase(className)) {
return new Circle(args[0]);
}
else return null;
}
}
public class JavaDemo {
public static void main(String[] args) {
IGraphical iga = Factory.getInstance("triangle", 1.1,2.2,3.3,4.4,11.11,22.22,33.33,44.44,111.111,222.222,333.333,444.444);
iga.paint();
IGraphical igb = Factory.getInstance("circle", 88.11);
igb.paint();
}
}
abstract class AbstractShape {
public abstract double area();
public abstract double perimeter();
}
class Circle extends AbstractShape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double area() {
return 3.1415926 * this.radius * this.radius;
}
@Override
public double perimeter() {
return 2 * 3.1415926 * this.radius;
}
}
class Rectangle extends AbstractShape {
private double length;
private double width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
@Override
public double area() {
return this.length * this.width;
}
@Override
public double perimeter() {
return 2 * (this.length + this.width);
}
}
class Factory {
public static AbstractShape getInstance(String className, double ... args) {
if ("circle".equalsIgnoreCase(className)) return new Circle(args[0]);
else if ("rectangle".equalsIgnoreCase(className)) return new Rectangle(args[0], args[1]);
else return null;
}
}
public class JavaDemo {
public static void main(String[] args) {
AbstractShape asa = Factory.getInstance("circle", 1.1);
AbstractShape asb = Factory.getInstance("rectangle", 1.5, 10.2);
System.out.println("【圆形】面积:" + asa.area() + ",周长:" + asa.perimeter());
System.out.println("【矩形】面积:" + asb.area() + ",周长:" + asb.perimeter());
}
}
泛型是从JDK1.5之后追加的,其主要目的是为了解决ClassCastException的问题,在进行对象的向下转型时可能存在安全隐患,而Java希望通过泛型来慢慢解决此问题。
案例:定义一个描述x与y坐标的处理类
class Point {
private Object x;
private Object y;
public Object getX() {
return x;
}
public void setX(Object x) {
this.x = x;
}
public Object getY() {
return y;
}
public void setY(Object y) {
this.y = y;
}
}
public class JavaDemo {
public static void main(String[] args) {
Point point = new Point();
// 第一步:根据需求进行内容的设置
point.setX(10); // 自动装箱
point.setY(20); // 自动装箱
// 第二步:获取数据
int x = (Integer)point.getX();
int y = (Integer)point.getY();
System.out.println("X坐标:" + x + ",Y坐标:" + y);
}
}
public class JavaDemo {
public static void main(String[] args) {
Point point = new Point();
// 第一步:根据需求进行内容的设置
point.setX(10); // 自动装箱
point.setY("北纬30度"); // 自动装箱
// 第二步:获取数据
int x = (Integer)point.getX();
int y = (Integer)point.getY(); // 此时编译不会出现问题,运行时才会报ClassCastException异常
System.out.println("X坐标:" + x + ",Y坐标:" + y);
}
}
避免项目中出现“ClassCastException”最好的做法是可以直接回避掉对象的强制转换,所以在JDK1.5之后提供了泛型技术。
泛型的本质:类中的属性、方法的参数与返回值的类型,可以在对象实例化时动态决定。
在类定义时,就需要明确地定义占位符(泛型标记)。
class Point { // T是Type地简写,可以定义多个泛型
private T x;
private T y;
public T getX() {
return x;
}
public void setX(T x) {
this.x = x;
}
public T getY() {
return y;
}
public void setY(T y) {
this.y = y;
}
}
此时Point类中的x与y属性的数据类型并不确定,而是由外部来决定。
关于默认的泛型类型:为兼容之前项目的代码,所以如不设置泛型类型,则自动使用Object作为默认类型。但编译中会出现警告信息。
泛型定义完成后,可以在实例化对象时进行泛型的设置,而一旦设置之后,属性的类型就与当前的对象直接绑定了。
public class JavaDemo {
public static void main(String[] args) {
Point point = new Point<>();
// 第一步:根据需求进行内容的设置
point.setX(10); // 自动装箱
point.setY("北纬30度"); // 此时编译则会报错
// 第二步:获取数据
int x = (Integer)point.getX();
int y = (Integer)point.getY();
System.out.println("X坐标:" + x + ",Y坐标:" + y);
}
}
public class JavaDemo {
public static void main(String[] args) {
Point point = new Point(); // 此时,point这个对象里,所有的T都被替换成了Integer
// 第一步:根据需求进行内容的设置
point.setX(10); // 自动装箱
point.setY(20); // 自动装箱
// 第二步:获取数据
int x = point.getX();
int y = point.getY();
System.out.println("X坐标:" + x + ",Y坐标:" + y);
}
}
使用泛型的注意事项:
虽然泛型帮助开发者解决了一系列由于对象强制转换而带来的安全隐患问题,但泛型也带来了新的问题:引用传递问题。
可以使用通配符”?“来解决泛型带来的引用传递的问题。
class Message {
private T content;
public T getContent() {
return content;
}
public void setContent(T content) {
this.content = content;
}
}
public class JavaDemo {
public static void main(String[] args) {
Message msgA = new Message<>();
Message msgB = new Message<>();
msgA.setContent(110);
fun(msgA);
msgB.setContent("pikaqiu");
fun(msgB);
}
public static void fun(Message> temp) {
System.out.println(temp.getContent());
}
}
通配符”?“:
“? extends 类”:设置泛型的上限
如定义”? extends Number“:表示该泛型类型只允许设置Number或Number的子类。
public class JavaDemo {
public static void main(String[] args) {
Message msgA = new Message<>();
Message msgB = new Message<>();
msgA.setContent(110);
fun(msgA);
msgB.setContent("pikaqiu");
fun(msgB); // 编译时会报错
}
public static void fun(Message extends Number> temp) { // 设置泛型上限
System.out.println(temp.getContent());
}
}
“? super 类”:设置泛型的下限
如定义”? super String“:表示该泛型类型只允许设置String或String的父类。
public class JavaDemo {
public static void main(String[] args) {
Message msgA = new Message<>();
Message msgB = new Message<>();
msgA.setContent(110);
fun(msgA);
msgB.setContent("pikaqiu");
fun(msgB); // 编译时会报错
}
public static void fun(Message super String> temp) { // 设置泛型下限
System.out.println(temp.getContent());
}
}
interface IMessage {
public String echo(T t);
}
泛型接口子类的实现方式:
实现方式一:在子类中设置泛型定义
class MessageImpl implements IMessage {
public String echo(S t) {
return "【ECHO】" + t;
}
}
public class JavaDemo {
public static void main(String[] args) {
IMessage msg = new MessageImpl<>();
System.out.println(msg.echo("pikaqiu"));
}
}
实现方式二:在子类实现父接口时直接定义具体泛型类型
class MessageImpl implements IMessage {
public String echo(String t) {
return "【ECHO】" + t;
}
}
public class JavaDemo {
public static void main(String[] args) {
IMessage msg = new MessageImpl();
System.out.println(msg.echo("pikaqiu"));
}
}
泛型方法不一定非要出现在泛型类中。
public class JavaDemo {
public static void main(String[] args) {
Integer[] num = fun(1, 2, 3);
for(int temp : num) {
System.out.print(temp + " ");
}
}
public static T[] fun(T ... args) {
return args;
}
}
使用泛型的工厂设计模式:
interface IMessage {
void send(String str);
}
class MessageImpl implements IMessage {
@Override
public void send(String str) {
System.out.println("消息发送:" + str);
}
}
class Factory {
public static T getInstance(String className) {
if ("MessageImpl".equalsIgnoreCase(className))
return (T)new MessageImpl();
else return null;
}
}
public class JavaDemo {
public static void main(String[] args) {
IMessage msg = Factory.getInstance("MessageImpl");
msg.send("pikaqiu");
}
}
在实际的项目开发过程中,要一直存在包的概念,利用包可以实现类的包装,以后所有的类都必须放在包里面。
当项目中有多人协同开发时,就有可能产生类的重名定义。
包相当于操作系统中的目录:同一个目录中不允许两个同名文件存在。
示例:
编写Hello.java文件:
package cn.pjh.demo; // 定义包,其中.表示分割子目录(子包)
public class Hello {
public static void main() {
System.out.println("Hello World!");
}
}
打包编译处理:javac -d . Hello.java
在程序执行时,一定要带着包名执行程序类:java cn.pjh.demo.Hello
完整的类名称时“包名.类名”。
利用包就可以将不同功能的类保存在不同的包之中,如果类之间需要互相调用,那么就需要使用import语句来导入其它包中的程序类。
示例:
package cn.pjh.util; // 定义包
public class Message {
public String getContent() {
return "pikaqiu";
}
}
package cn.pjh.test; // 定义包
import cn.pjh.util.Message; // 导入包
public class TestMessage {
public static void main(String[] args) {
Message msg = new Message();
System.out.println(msg.getContent());
}
}
编译命令:javac -d . *.java(由编译器自己决定编译顺序。)
注意:关于public class与class定义的区别?
程序类中定义的包名必须采用小写字母的形式来定义。
可使用“包名.”的形式来导入该包下的所有类,但这并不表示要进行全部的加载,会根据需要进行加载,所以使用“”*“和使用具体类的性能是完全相同的。
由于导入的不同包中,也可能存在相同类名,因此在调用的时候应该使用类的完整名称,否则编译时会出现错误。
示例:
package cn.pjh.util;
public class MyMath {
public static int add(int ... args) {
int sum = 0;
for (int temp : args) {
sum += temp;
}
return sum;
}
public static int sub(int x, int y) {
return x - y;
}
}
原始导入方法:
package cn.pjh.test;
import cn.pjh.util.MyMath;
public class TestMath {
public static void main(String[] args) {
System.out.println(MyMath.add(10, 20, 30));
System.out.println(MyMath.sub(30, 20));
}
}
从JDK1.5开始,对于类中全部由静态方法提供的特殊类时可以采用静态导入处理形式的。
静态导入:
package cn.pjh.test;
import static cn.pjh.util.MyMath.*;
public class TestMath {
public static void main(String[] args) {
System.out.println(add(10, 20, 30));
System.out.println(sub(30, 20));
}
}
当使用静态导入后,就好比该方法是直接定义在主类中的,可以由主方法直接调用。
jar文件:把开发完成后大量的*.class文件管理起来的压缩结构。
可使用jdk中提供的jar命令将文件打包成jar文件,可在命令行中输入“jar”来查看所有jar命令。
示例:
定义一个程序类:
package cn.pjh.util;
public class Message {
public String getContent() {
return "pikaqiu";
}
}
对程序打包编译:
配置CLASSPATH(每一个jar文件都是一个独立的程序路径,如果要想在Java程序中使用此路径,则必须通过CLASSPATH进行配置。)
建立测试类,直接导入Message类并且调用方法:
package cn.pjh.test;
public class TestMessage {
public static void main(String[] args) {
cn.pjh.util.Message msg = new cn.pjh.util.Message(); // 实例化类对象
System.out.println(msg.getContent());
}
}
正常编译TestMessage类并使用这个类:
出现“java.lang.NoClassDefFoundError”的错误:只有一种情况,*.jar包没有配置正确(可能为CLASSPATH发生了改变,类无法加载到了)。
JDK1.9之后出现的模块化操作:
No | 访问范围 | private | default | protected | public |
---|---|---|---|---|---|
1 | 同包中的同类 | √ | √ | √ | √ |
2 | 同包中的不同类 | √ | √ | √ | |
3 | 不同包的子类 | √ | √ | ||
4 | 不同包的所有类 | √ |
单例设计模式:只保留一个实例化对象的设计模式。
案例(饿汉式单例设计演化):
非单例设计:
class Singleton {
public void print() {
System.out.println("pikaqiu");
}
}
public class JavaDemo {
public static void main(String[] args) {
// 可产生多个实例化对象
Singleton instanceA = new Singleton();
Singleton instanceB = new Singleton();
Singleton instanceC = new Singleton();
instanceA.print();
instanceB.print();
instanceC.print();
}
}
单例演化:
如果一个类只允许提供有一个实例化对象,那么为了避免有新的实例化对象产生,则一定要将构造方法私有化;
class Singleton {
private Singleton() {} // 构造方法私有化
public void print() {
System.out.println("pikaqiu");
}
}
public class JavaDemo {
public static void main(String[] args) {
Singleton instance = null; // 声明对象
instance = new Singleton(); // 错误:Singleton()可以在Singleton中访问private
instance.print();
}
}
而private私有化的特点是,不能在类外部访问,却能在类内部访问,那么则可以使用本类属性来进行实例化;
class Singleton {
Singleton instance = new Singleton(); // 本类属性可调用私有化的构造方法
private Singleton() {} // 构造方法私有化
public void print() {
System.out.println("pikaqiu");
}
}
public class JavaDemo {
public static void main(String[] args) {
Singleton instance = null; // 声明对象
// instance = new Singleton();
// instance.print();
}
}
而普通属性在没有实例化对象时也是无法访问的,那么如何在没有实例化对象时获取属性呢?这就只有static属性可以做到了;
class Singleton {
static Singleton instance = new Singleton(); // 静态属性,无需实例化对象就可以调用
private Singleton() {} // 构造方法私有化
public void print() {
System.out.println("pikaqiu");
}
}
public class JavaDemo {
public static void main(String[] args) {
Singleton instance = null; // 声明对象
instance = Singleton.instance;
instance.print(); // 正常输出
}
}
而属性都应该进行封装,所以该属性应该通过static方法来进行获取;
class Singleton {
private static Singleton instance = new Singleton(); // 封装静态属性
private Singleton() {} // 构造方法私有化
public static Singleton getInstance() { // 静态方法返回封装了的静态属性
return instance;
}
public void print() {
System.out.println("pikaqiu");
}
}
public class JavaDemo {
public static void main(String[] args) {
Singleton instance = null; // 声明对象
instance = Singleton.getInstance();
instance.print(); // 正常输出
}
}
然而如果想要实例化对象只有一个,则需要使用final。
// 饿汉式
class Singleton {
private static final Singleton INSTANCE = new Singleton(); // 私有的静态常量
private Singleton() {} // 构造方法私有化
public static Singleton getInstance() { // 静态方法返回私有的静态属性
return INSTANCE;
}
public void print() {
System.out.println("pikaqiu");
}
}
public class JavaDemo {
public static void main(String[] args) {
Singleton instance = null; // 声明对象,并实例化
instance = Singleton.getInstance();
instance.print(); // 正常输出
}
}
分类:
饿汉式:在系统加载类时自动提供类的实例化对象。(声明对象时就实例化)
懒汉式:在第一次使用类时提供类的实例化对象。(首次使用时才实例化)
// 懒汉式
class Singleton {
private static Singleton instance; // 去掉final,因为final会使得instance不可更改
private Singleton() {} // 构造方法私有化
public static Singleton getInstance() {
if (null == instance) { // 第一次使用
instance = new Singleton(); // 实例化对象
}
return instance;
}
public void print() {
System.out.println("pikaqiu");
}
}
public class JavaDemo {
public static void main(String[] args) {
Singleton instance = null; // 声明对象
instance = Singleton.getInstance(); // 第一次调用时实例化
instance.print(); // 正常输出
}
}
步骤总结:
class Color {
private static final Color RED = new Color("红色"); // 私有的静态常量
private static final Color GREEN = new Color("绿色"); // 私有的静态常量
private static final Color BLUE = new Color("蓝色"); // 私有的静态常量
private String title;
private Color(String title) { // 构造方法私有化
this.title = title;
}
public static Color getInstatnce(String color) { // 获取静态常量的静态方法
switch(color) {
case "red": return RED;
case "green": return GREEN;
case "blue": return BLUE;
default: return null;
}
}
public String toString() {
return this.title;
}
}
public class JavaDemo {
public static void main(String[] args) {
Color color = Color.getInstatnce("green");
System.out.println(color);
}
}
enum Color { // 枚举类
RED, GREEN, BLUE; // 实例化对象
}
public class JavaDemo {
public static void main(String[] args) {
Color color = Color.RED; // 获取实例化对象
System.out.println(color);
}
}
多例设计与枚举设计虽然可以实现相同的功能,但使用枚举可以在程序编译时就判断所使用的实例化对象是否存在。
可以使用values()方法来获取所有的枚举对象:
enum Color { // 枚举类
RED, GREEN, BLUE; // 实例化对象
}
public class JavaDemo {
public static void main(String[] args) {
for (Color color : Color.values()) {
System.out.println(color);
}
}
}
可以在switch中进行枚举项的判断:
enum Color { // 枚举类
RED, GREEN, BLUE; // 实例化对象
}
public class JavaDemo {
public static void main(String[] args) {
Color color = Color.RED;
switch(color) { // 支持使用枚举项
case RED:
System.out.println("红色");
break;
case GREEN:
System.out.println("绿色");
break;
case BLUE:
System.out.println("蓝色");
break;
}
}
}
严格意义上来讲,枚举并不算一种新的结构,它的本质是一个类,而这个类默认会继承Enum类。
Enum类的定义:
public abstract class Enum
> extends Object implements Comparable , Serializable
Enum类的方法:
No | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | protected Enum(String name, int ordinal) | 构造 | |
2 | public final String name() | 普通 | 获取对象名字 |
3 | public final int ordinal() | 普通 | 获取对象序号(由定义顺序决定) |
enum Color { // 枚举类
RED, GREEN, BLUE; // 实例化对象
}
public class JavaDemo {
public static void main(String[] args) {
for (Color color : Color.values()) {
System.out.println(color.ordinal() + "-" + color.name());
}
}
}
面试题:请解释enum与Enum的区别?
enum Color { // 枚举类
RED("红色"), GREEN("绿色"), BLUE("蓝色"); // 枚举对象要写在首行
private String title; // 定义属性
private Color(String title) {
this.title = title;
}
public String toString() {
return this.title;
}
}
public class JavaDemo {
public static void main(String[] args) {
for (Color color : Color.values()) {
System.out.println(color.ordinal() + "-" + color.name() + "-" + color);
}
}
}
interface IMessage {
public String getMessage();
}
enum Color implements IMessage { // 枚举类
RED("红色"), GREEN("绿色"), BLUE("蓝色"); // 枚举对象要写在首行
private String title; // 定义属性
private Color(String title) {
this.title = title;
}
public String toString() {
return this.title;
}
@Override
public String getMessage() {
return this.title;
}
}
public class JavaDemo {
public static void main(String[] args) {
IMessage msg = Color.RED;
System.out.println(msg.getMessage());
}
}
enum Color { // 枚举类
RED("红色") {
public String getMessage() {
return this.toString();
}
}, GREEN("绿色") {
public String getMessage() {
return this.toString();
}
}, BLUE("蓝色") {
public String getMessage() {
return this.toString();
}
}; // 枚举对象要写在首行
private String title; // 定义属性
private Color(String title) {
this.title = title;
}
public String toString() {
return this.title;
}
public abstract String getMessage();
}
public class JavaDemo {
public static void main(String[] args) {
System.out.println(Color.RED.getMessage());
}
}
enum Sex {
MALE("男"), FEMALE("女");
private String title;
private Sex(String title) {
this.title = title;
}
public String toString() {
return this.title;
}
}
class Person {
private String name;
private int age;
private Sex sex;
public Person(String name, int age, Sex sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
public String toString() {
return "姓名:" + this.name + ",年龄:" + this.age + ",性别:" + this.sex;
}
}
public class JavaDemo {
public static void main(String[] args) {
System.out.println(new Person("张三", 20, Sex.MALE));
}
}
public class JavaDemo {
public static void main(String[] args) {
System.out.println("【1】****** 程序开始执行 ******");
System.out.println("【2】****** 数学计算:" + (10 / 2) + " ******");
System.out.println("【3】****** 程序执行结束 ******");
}
}
public class JavaDemo {
public static void main(String[] args) {
System.out.println("【1】****** 程序开始执行 ******");
System.out.println("【2】****** 数学计算:" + (10 / 0) + " ******");
System.out.println("【3】****** 程序执行结束 ******");
// 执行后输出:
// 【1】****** 程序开始执行 ******
// Exception in thread "main" java.lang.ArithmeticException: / by zero
}
}
在出现错误后,整个的程序将不会按照既定的方式进行执行,而是中断了执行。
为了保证程序出现了非致命错误,程序依然可以正常完成,所以就需要由一个完善的异常处理机制,以保证程序的顺利执行。
try {
// 可能出现异常的语句
}
[catch(异常类型 异常对象) {
// 异常处理的语句
} catch(异常类型 异常对象) {
// 异常处理的语句
} catch(异常类型 异常对象) {
// 异常处理的语句
} ... ]
[finally {
// 不管异常是否处理都要执行的语句
}]
在此格式中,可以使用的组合为:try … catch、try … catch … finally、try … finally。
public class JavaDemo {
public static void main(String[] args) {
System.out.println("【1】****** 程序开始执行 ******");
try {
System.out.println("【2】****** 数学计算:" + (10 / 0) + " ******"); // 会出现异常的语句
} catch (ArithmeticException e) {
System.out.println("【C】处理异常:" + e); // 处理异常
}
System.out.println("【3】****** 程序执行结束 ******");
// 执行后输出:
// 【1】****** 程序开始执行 ******
// 【C】处理异常:java.lang.ArithmeticException: / by zero
// 【3】****** 程序执行结束 ******
}
}
此时,即便发生了异常,程序也可以正常执行结束,但是此时在进行异常处理时直接输出的时一个异常类的对象,那么该对象直接打印(调用toString())所得到的异常信息并不完整,可以使用异常类中提供的printStackTrace()方法:
public class JavaDemo {
public static void main(String[] args) {
System.out.println("【1】****** 程序开始执行 ******");
try {
System.out.println("【2】****** 数学计算:" + (10 / 0) + " ******"); // 会出现异常的语句
} catch (ArithmeticException e) {
e.printStackTrace(); // 打印异常的完整信息
}
System.out.println("【3】****** 程序执行结束 ******");
}
}
// 有异常
public class JavaDemo {
public static void main(String[] args) {
System.out.println("【1】****** 程序开始执行 ******");
try {
System.out.println("【2】****** 数学计算:" + (10 / 0) + " ******"); // 有异常
} catch (ArithmeticException e) {
e.printStackTrace(); // 打印异常的完整信息
} finally {
System.out.println("【F】不管是否出现异常,都会执行。");
}
System.out.println("【3】****** 程序执行结束 ******");
}
}
// 无异常
public class JavaDemo {
public static void main(String[] args) {
System.out.println("【1】****** 程序开始执行 ******");
try {
System.out.println("【2】****** 数学计算:" + (10 / 1) + " ******"); // 无异常
} catch (ArithmeticException e) {
e.printStackTrace(); // 打印异常的完整信息
} finally {
System.out.println("【F】不管是否出现异常,都会执行。");
}
System.out.println("【3】****** 程序执行结束 ******");
}
}
public class JavaDemo {
public static void main(String[] args) {
System.out.println("【1】****** 程序开始执行 ******");
try {
int x = Integer.parseInt(args[0]);
int y = Integer.parseInt(args[1]);
System.out.println("【2】****** 数学计算:" + (x / y) + " ******");
} catch (ArithmeticException e) {
e.printStackTrace(); // 打印异常的完整信息
} finally {
System.out.println("【F】不管是否出现异常,都会执行。");
}
System.out.println("【3】****** 程序执行结束 ******");
}
}
此时程序可能产生三类异常:
执行结果如下:
所以即使有了异常处理语句,但如果没有进行正确的异常捕获,那么程序也会导致中断(finally的代码依然执行)
public class JavaDemo {
public static void main(String[] args) {
System.out.println("【1】****** 程序开始执行 ******");
try {
int x = Integer.parseInt(args[0]);
int y = Integer.parseInt(args[1]);
System.out.println("【2】****** 数学计算:" + (x / y) + " ******");
} catch (ArithmeticException e) {
e.printStackTrace(); // 打印异常的完整信息
} catch (NumberFormatException e) {
e.printStackTrace(); // 打印异常的完整信息
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace(); // 打印异常的完整信息
} finally {
System.out.println("【F】不管是否出现异常,都会执行。");
}
System.out.println("【3】****** 程序执行结束 ******");
}
}
在整个异常处理流程中,实际上操作的还是一个异常类的实例化对象,所以这个异常类的实例化对象的类型就成为了理解异常处理的核心关键所在。
ArithmeticException的包:
java.lang.Object
java.lang.Throwable
java.lang.Exception
java.lang.RuntimeException
java.lang.ArithmeticException
NumberFormatException的包:
java.lang.Object
java.lang.Throwable
java.lang.Exception
java.lang.RuntimeException
java.lang.IllegalArgumentException
java.lang.NumberFormatException
ArrayIndexOutOfBoundsException的包:
java.lang.Object
java.lang.Throwable
java.lang.Exception
java.lang.RuntimeException
java.lang.IndexOutOfBoundsException
java.lang.ArrayIndexOutOfBoundsException
可以发现,程序中处理异常最大的类型就是Throwable类。
Throwable类:
由于异常产生时,会产生异常的实例化对象,那么按照对象的引用原则,其可以自动向父类转型,所以实际上所有的异常都可以使用Exception来处理:
public class JavaDemo {
public static void main(String[] args) {
System.out.println("【1】****** 程序开始执行 ******");
try {
int x = Integer.parseInt(args[0]);
int y = Integer.parseInt(args[1]);
System.out.println("【2】****** 数学计算:" + (x / y) + " ******");
} catch (Exception e) {
e.printStackTrace(); // 打印异常的完整信息
} finally {
System.out.println("【F】不管是否出现异常,都会执行。");
}
System.out.println("【3】****** 程序执行结束 ******");
}
}
但如果都用Exception来处理异常,会导致描述的错误信息不明确,所以分开处理异常是更加明确的处理方式。
class MyMath {
// 下面这段代码可能会产生异常,如果产生异常,则需要调用处进行处理
public static int div(int x, int y) throws Exception {
return x / y;
}
}
public class JavaDemo {
public static void main(String[] args) {
System.out.println(MyMath.div(10, 2)); // 编译时则会出现“Error:(9, 38) java: 未报告的异常错误java.lang.Exception; 必须对其进行捕获或声明以便抛出”
}
}
class MyMath {
// 下面这段代码可能会产生异常,如果产生异常,则需要调用处进行处理
public static int div(int x, int y) throws Exception {
return x / y;
}
}
public class JavaDemo {
public static void main(String[] args) {
try {
System.out.println(MyMath.div(10, 2)); // 增加try...catch,可正常输出
} catch (Exception e) {
e.printStackTrace();
}
}
}
class MyMath {
// 下面这段代码可能会产生异常,如果产生异常,则需要调用处进行处理
public static int div(int x, int y) throws Exception {
return x / y;
}
}
public class JavaDemo {
public static void main(String[] args) throws Exception { // 主方法继续向上抛出异常
System.out.println(MyMath.div(10, 0)); // 执行时抛出ArithmeticException异常
}
}
public class JavaDemo {
public static void main(String[] args) {
try {
throw new Exception("自己抛着玩的异常");
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 执行时输出:java.lang.Exception: 自己抛着玩的异常
class MyMath {
public static int div(int x, int y) throws Exception { // 向上抛出异常
int temp = 0;
System.out.println("****** 【START】除法计算开始。");
try {
temp = x / y;
} finally {
System.out.println("****** 【END】除法计算结束。");
}
return temp;
}
}
public class JavaDemo {
public static void main(String[] args) {
try {
System.out.println(MyMath.div(10, 0));
} catch (Exception e) {
e.printStackTrace();
}
}
}
(个人发现的小知识点:如向上抛出异常,则运行时会先执行抛出异常中的finally块,再执行调用处的catch块,再执行调用处的finally块。)
灵活可选的异常处理类:RuntimeException类,其子类可以不需要调用处强制进行处理
面试题:请解释RuntimeException与Exception的区别?并列举出几个常见的RuntimeException异常。
class BombException extends Exception {
public BombException(String msg) {
super(msg);
}
}
class Food {
public static void eat(int num) throws BombException{
if (10 < num) throw new BombException("吃太多了,肚子爆了");
else System.out.println("正常吃,不怕吃胖。");
}
}
public class JavaDemo {
public static void main(String[] args) throws Exception {
Food.eat(11);
}
}
class BombException extends RuntimeException {
public BombException(String msg) {
super(msg);
}
}
class Food {
public static void eat(int num) throws BombException{
if (10 < num) throw new BombException("吃太多了,肚子爆了");
else System.out.println("正常吃,不怕吃胖。");
}
}
public class JavaDemo {
public static void main(String[] args) {
Food.eat(11);
}
}
public class JavaDemo {
public static void main(String[] args) {
int x = 10;
assert x == 100 : "x的内容不是100";
System.out.println(x); // 输出:10
}
}
执行断言:在执行程序时加入参数-ea
执行命令:java -ea JavaDemo
执行结果:
Exception in thread "main" java.lang.AssertionError: x的内容不是100
at JavaDemo.main(JavaDemo.java:4)
class Outer { // 外部类
private String msg = "piakqiu"; // 私有成员属性
public void fun() { // 普通方法
Inner in = new Inner(); // 实例化内部类对象
in.print(); // 调用内部类方法
}
class Inner { // 内部类
public void print() {
System.out.println(Outer.this.msg); // 打印外部类的私有属性
}
}
}
public class JavaDemo {
public static void main(String[] args) {
Outer out = new Outer(); // 实例化外部类对象
out.fun(); // 调用外部类方法
}
}
// 不使用内部类
class Outer { // 外部类
private String msg = "piakqiu"; // 私有成员属性
public void fun() { // 普通方法
// 思考五:需要将当前对象Outer传递到Inner类中
Inner in = new Inner(this); // 实例化内部类对象
in.print(); // 调用内部类方法
}
// 思考一:msg属性如果要被外部访问,则需要提供getter方法
public String getMsg() {
return this.msg;
}
}
class Inner { // 内部类
// 思考三:Inner这个类对象实例化时需要Outer类的引用
private Outer out;
// 思考四:应该通过Inner类的构造方法获取Outer类的对象
public Inner(Outer out) {
this.out = out;
}
public void print() {
// 思考二:如果要想调用外部类中的getter方法,那么一定要有Outer类的对象
System.out.println(this.out.getMsg()); // 打印外部类的私有属性
}
}
public class JavaDemo {
public static void main(String[] args) {
Outer out = new Outer(); // 实例化外部类对象
out.fun(); // 调用外部类方法
}
}
可以看到,上述代码的主要目的就是为了让Inner内部类可以访问Outer外部类中的私有属性。
class Outer { // 外部类
private String msg = "piakqiu"; // 私有成员属性
public void fun() { // 普通方法
Inner in = new Inner(); // 实例化内部类对象
in.print(); // 调用内部类方法
System.out.println(in.info); // 访问内部类的私有属性
}
class Inner { // 内部类
private String info = "今天天气不好,收衣服啦!";
public void print() {
System.out.println(Outer.this.msg); // 打印外部类的私有属性
}
}
}
public class JavaDemo {
public static void main(String[] args) {
Outer out = new Outer(); // 实例化外部类对象
out.fun(); // 调用外部类方法
}
}
内部类与外部类之间私有操作的访问就不再需要通过setter/getter以及其它间接的方式来完成,可以直接进行操作。
内部类也是一个类,所以外部依然可以产生内部类的实例化对象:
外部类.内部类 内部类对象名称 = new 外部类().new 内部类();
在内部类编译完成后,会自动形成一个“Outer I n n e r . c l a s s ” 的 文 件 , 其 中 “ Inner.class”的文件,其中“ Inner.class”的文件,其中“”这个符号换到程序之中就变为了“.”,所以内部类的全称为:“外部类.内部类”。
抽象类与接口中都可以定义内部结构。
interface IChannel { // 定义接口
public void send(IMessage msg); // 发送消息
interface IMessage { // 内部接口
public String getContent(); // 获取消息内容
}
}
class ChannelImpl implements IChannel {
public void send(IMessage msg) {
System.out.println("发送消息:" + msg.getContent());
}
class MessageImpl implements IMessage {
public String getContent() {
return "pikaqiu";
}
}
}
public class JavaDemo {
public static void main(String[] args) {
IChannel channel = new ChannelImpl();
channel.send(((ChannelImpl)channel).new MessageImpl());
}
}
interface IChannel { // 定义接口
public void send(); // 发送消息
abstract class AbstractMessage {
public abstract String getContent();
}
}
class ChannelImpl implements IChannel {
public void send() {
AbstractMessage msg = new MessageImpl();
System.out.println("发送消息:" + msg.getContent());
}
class MessageImpl extends AbstractMessage {
public String getContent() {
return "pikaqiu";
}
}
}
public class JavaDemo {
public static void main(String[] args) {
IChannel channel = new ChannelImpl();
channel.send();
}
}
interface IChannel {
public void send();
class ChannelImpl implements IChannel {
public void send() {
System.out.println("pikaqiu");
}
}
public static IChannel getInstance() {
return new ChannelImpl();
}
}
public class JavaDemo {
public static void main(String[] args) {
IChannel channel = IChannel.getInstance();
channel.send();
}
}
如果在内部类上使用了static,那么这个内部类就变为了“外部类”,因为static定义的都是独立于类的结构,所以static定义的类就相当于时一个独立的程序类了。
实例化static内部类对象:
外部类.内部类 内部类对象名称 = new 外部类.内部类();
class Outer {
private static final String MSG = "pikaqiu";
static class Inner {
public void print() {
System.out.println(Outer.MSG);
}
}
}
public class JavaDemo {
public static void main(String[] args) {
Outer.Inner in = new Outer.Inner();
in.print();
}
}
static定义内部类的形式并不常用,static定义内部接口的形式最为常用:
interface IMessageWarp { // 消息包装类
static interface IMessage {
public String getContent();
}
static interface IChannel {
public boolean connect(); // 消息的发送通道
}
public static void send(IMessage msg, IChannel channel) {
if (channel.connect()) System.out.println(msg.getContent());
else System.out.println("消息通道无法建立,消息发送失败!");
}
}
class DefaultMessage implements IMessageWarp.IMessage {
public String getContent() {
return "pikaqiu";
}
}
class NetChannel implements IMessageWarp.IChannel {
public boolean connect() {
return true;
}
}
public class JavaDemo {
public static void main(String[] args) {
IMessageWarp.send(new DefaultMessage(), new NetChannel());
}
}
在方法中定义的内部类:
class Outer {
private String msg = "pikaqiu";
public void fun(long time) {
class Inner { // 内部类
public void print() {
System.out.println(Outer.this.msg);
System.out.println(time);
}
}
new Inner().print(); // 方法中直接实例化内部类对象
}
}
public class JavaDemo {
public static void main(String[] args) {
new Outer().fun(2390239023L);
}
}
内部类可以直接访问外部类中的私有成员,也可以直接访问方法中的参数,但对于方法中参数的直接访问,是从JDK1.8开始支持的。
而在JDK1.8之前,如果方法中定义的内部类要想访问方法中的参数,则参数前必须追加final:
class Outer {
private String msg = "pikaqiu";
public void fun(final long time) {
final String info = "我很好";
class Inner { // 内部类
public void print() {
System.out.println(Outer.this.msg);
System.out.println(time);
System.out.println(info);
}
}
new Inner().print(); // 方法中直接实例化内部类对象
}
}
public class JavaDemo {
public static void main(String[] args) {
new Outer().fun(2390239023L);
}
}
而取消了final这样的限制,主要是为了其扩展的函数式编程准备的功能。
interface IMessage {
public void send(String str);
}
class MessageImpl implements IMessage {
public void send(String str) {
System.out.println(str);
}
}
public class JavaDemo {
public static void main(String[] args) {
IMessage msg = new MessageImpl();
msg.send("pikaqiu");
}
}
上述代码中,如IMessage接口中的MessageImpl子类只使用唯一一次,那么则可以利用匿名内部类来实现:
interface IMessage {
public void send(String str);
}
public class JavaDemo {
public static void main(String[] args) {
IMessage msg = new IMessage() { // 匿名内部类
public void send(String str) {
System.out.println(str);
}
};
msg.send("pikaqiu");
}
}
(上述代码编译之后,会多生成一个JavaDemo$1.class的文件)
有时为了更加方便地体现出匿名内部类的使用,往往利用静态方法来左一个内部的匿名内部类实现:
interface IMessage {
public void send(String str);
public static IMessage getInstance() {
return new IMessage() {
public void send(String str) {
System.out.println(str);
}
};
}
}
public class JavaDemo {
public static void main(String[] args) {
IMessage.getInstance().send("pikaqiu");
}
}
与内部类相比,匿名内部类只是一个没有名字的,只能够使用一次的,并且结构固定的一个子类。
从JDK1.8开始,为了简化代码的开发,专门提供Lambda表达式,利用此表达式可实现函数式编程。函数式编程较著名的语言:hackell、Scala。
利用函数式编程可以避免面向对象编程中一些繁琐的处理:
传统面向对象的处理:
interface IMessage {
public void send(String str);
}
public class JavaDemo {
public static void main(String[] args) {
IMessage msg = new IMessage() {
public void send(String str) {
System.out.println("消息发送:" + str);
}
};
msg.send("pikaqiu");
}
}
实际上核心功能只有:“System.out.println(“消息发送:” + str);”
使用Lambda表达式:
interface IMessage {
public void send(String str);
}
public class JavaDemo {
public static void main(String[] args) {
IMessage msg = (str)->{
System.out.println("发送消息:" + str);
};
msg.send("pikaqiu");
}
}
Lambda表达式的重要实现要求:SAM(Single Abstract Method,单个抽象方法)。
函数式接口:接口里只提供有一个抽象方法。
只有函数式接口才能使用Lambda表达式。
@FunctionalInterface // 函数式接口
interface IMessage {
public void send(String str); // 抽象方法
public default void print() {}; // 普通方法(接口中使用default定义的方法)
}
public class JavaDemo {
public static void main(String[] args) {
IMessage msg = (str)->{
System.out.println("发送消息:" + str);
};
msg.send("pikaqiu");
msg.print();
}
}
Lambda表达式的格式:
方法没有参数:()->{};
@FunctionalInterface
interface IMessage {
public void send();
}
public class JavaDemo {
public static void main(String[] args) {
IMessage msg = ()->{
System.out.println("发送消息");
};
msg.send(); // 输出:发送消息
}
}
方法有参数:(参数, 参数)->{};
@FunctionalInterface
interface IMath {
public int add(int x, int y);
}
public class JavaDemo {
public static void main(String[] args) {
IMath math = (t1, t2)->{
return t1 + t2;
};
System.out.println(math.add(10, 20)); // 输出:30
}
}
只有一行语句返回:
方法没有参数:()->语句;
@FunctionalInterface
interface IMessage {
public void send();
}
public class JavaDemo {
public static void main(String[] args) {
IMessage msg = ()->System.out.println("发送消息");
msg.send(); // 输出:发送消息
}
}
方法有参数:(参数, 参数)->语句;
@FunctionalInterface
interface IMath {
public int add(int x, int y);
}
public class JavaDemo {
public static void main(String[] args) {
IMath math = (t1, t2)->t1 + t2;
System.out.println(math.add(10, 20)); // 输出:30
}
}
引用数据类型最大的特点就是可以进行内存的指向处理,但在传统的开发之中一直所使用的只是对象的引用操作,而从JDK1.8之后也提供有了方法的引用,即:不同的方法名称可以描述同一个方法。
方法引用的形式:
引用静态方法:
类名称 :: static 方法名称
如 String类 的 valueOf() 方法就是静态方法:public static String valuesOf(int i)
@FunctionalInterface
// P:参数;R:返回值。
interface IFunction {
public R change(P p);
}
public class JavaDemo {
public static void main(String[] args) {
// 这里的“String :: valueOf”是利用函数式编程给IFunction接口的change抽象方法定义的方法体
IFunction fun = String :: valueOf;
String str = fun.change(100);
System.out.println(str.length()); // 输出:3
}
}
引用某个实例对象的方法:
实例化对象 :: 普通方法的方法名称
如 String类 的 toUpperCase() 方法就需要实例化对象后才可调用:public String toUpperCase()
@FunctionalInterface
// P:参数;R:返回值。
interface IFunction {
public R upper();
}
public class JavaDemo {
public static void main(String[] args) {
// 这里的“pikaqiu”为实例化对象
IFunction fun = "pikaqiu" :: toUpperCase;
System.out.println(fun.upper()); // 输出:PIKAQIU
}
}
引用特定类的方法:
特定类 :: 普通方法的方法名称
如调用 String类 的 compareTo() 方法:public int compareTo(String anotherString)
@FunctionalInterface
// P:参数;R:返回值。
interface IFunction {
public int compare(P p1, P p2);
}
public class JavaDemo {
public static void main(String[] args) {
IFunction fun = String :: compareTo;
System.out.println(fun.compare("A", "a")); // 输出:-32
}
}
引用构造方法(最具杀伤力):
类名称 :: new
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return "姓名:" + this.name + ",年龄:" + this.age;
}
}
@FunctionalInterface
interface IFunction {
public R create(String s, int i);
}
public class JavaDemo {
public static void main(String[] args) {
IFunction fun = Person :: new;
System.out.println(fun.create("张三", 20));
}
}
方法引用,更多情况下只是弥补对于引用的支持功能。
在系统中专门有一个 java.util.function 的开发包,可以直接使用里面提供的函数式接口。
内建函数式接口:
功能型函数式接口:接收参数,有返回。
接口定义:
@FunctionalInterface
public interface Function {
public R apply(T t)
}
接口使用:
import java.util.function.*;
public class JavaDemo {
public static void main(String[] args) {
// public boolean startsWith(String prefix)
Function fun = "**hello" :: startsWith;
System.out.println(fun.apply("**")); // 输出:true
}
}
消费型函数式接口:接收参数,无返回。
接口定义:
@FunctionalInterface
public interface Consumer {
public void accept(T t)
}
接口使用:
import java.util.function.*;
public class JavaDemo {
public static void main(String[] args) {
// public void println(String x)
Consumer con = System.out :: println;
con.accept("pikaqiu"); // 输出:pikaqiu
}
}
供给型函数式接口:不接收参数,有返回。
接口定义:
@FunctionalInterface
public interface Supplier {
public T get()
}
接口使用:
import java.util.function.*;
public class JavaDemo {
public static void main(String[] args) {
// public String toLowerCase()
Supplier sup = "PiKaQiu" :: toLowerCase;
System.out.println(sup.get()); // 输出:pikaqiu
}
}
断言型函数式接口:接收参数,返回boolean类型。
接口定义:
@FunctionalInterface
public interface Predicate {
public boolean test(T t)
}
接口使用:
import java.util.function.*;
public class JavaDemo {
public static void main(String[] args) {
// public boolean equalsIgnoreCase(String anotherString)
Predicate pre = "pikaqiu" :: equalsIgnoreCase;
System.out.println(pre.test("PIKAQIU")); // 输出:true
}
}
个人整理:
类型 | 接口名 | 方法名 | 是否接收参数 | 返回类型 |
---|---|---|---|---|
功能型 | Function | apply() | √ | 任意 |
消费型 | Consumer | accept() | √ | 无 |
供给型 | Supplier | get() | × | 任意 |
断言型 | Predicate | test() | √ | boolean |
class Node {
private E data;
private Node next;
public Node(E data) {
this.data = data;
}
public E getData() {
return data;
}
public void setData(E data) {
this.data = data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
}
public class JavaDemo {
public static void main(String[] args) {
Node node1 = new Node<>("火车头");
Node node2 = new Node<>("车厢一");
Node node3 = new Node<>("车厢二");
Node node4 = new Node<>("车厢三");
node1.setNext(node2);
node2.setNext(node3);
node3.setNext(node4);
print(node1);
}
public static void print(Node> node) {
if (null != node) { // 不为空节点
System.out.println(node.getData());
print(node.getNext()); // 递归调用
}
}
}
但是实际上,真实的使用者只关心数据的存储与获取,所以应作如下设计:
interface ILink { // 设置泛型避免安全隐患
public void add(E e); // 增加数据
}
class LinkImpl implements ILink {
private class Node { // 保存节点
private E data; // 保存数据
private Node next; // 保存下一个节点
public Node(E data) {
this.data = data;
}
// 第一次调用:this = LinkImpl.root;
// 第二次调用:this = LinkImpl.root.next;
// 第三次调用:this = LinkImpl.root.next.next;
public void addNode(Node newNode) { // 保存新节点
// 当前节点的下一个节点为null,则保存当前节点为下一个节点
if (null == this.next) this.next = newNode;
// 当前节点的下一个节点不为null,则递归调用
else this.next.addNode(newNode); //
}
}
// ———————————— 以下为Link类中定义的成员 ————————————
private Node root; // 保存根节点
// ———————————— 以下为Link类中定义的方法 ————————————
public void add(E e) {
if (null == e) return; // 如增加的数据为null,则方法调用直接结束
Node newNode = new Node(e);
if (null == this.root) this.root = newNode; // 如没有根节点,则增加的节点即为根节点
else this.root.addNode(newNode);
}
}
public class JavaDemo {
public static void main(String[] args) {
ILink link = new LinkImpl<>();
link.add("火车头");
link.add("车厢一");
link.add("车厢二");
link.add("车厢三");
}
}
获取集合个数:public int size()
对链表的数据个数进行统计:增加数据统计,并且当增加或删除数据时都应该对个数进行修改。
实现步骤:
在ILink接口里追加获取数据个数的方法:
interface ILink { // 设置泛型避免安全隐患
public void add(E e); // 增加数据
public int size(); // 获取数据的个数
}
在LinkImpl子类里追加获取数据个数的方法:
private int count; // 保存数据个数
在add()方法里进行数据个数的追加:
public void add(E e) {
if (null == e) return; // 如增加的数据为null,则方法调用直接结束
Node newNode = new Node(e);
if (null == this.root) this.root = newNode; // 如没有根节点,则增加的节点即为根节点
else this.root.addNode(newNode);
this.count ++;
}
在LinkImpl子类里返回数据的个数:
public int size() {
return this.count;
}
(完整代码在 课时142)
空集合判断:public boolean isEmpty()
空集合:链表中尚未保存数据。
实现步骤:
在ILink接口里追加判断空集合的方法:
interface ILink { // 设置泛型避免安全隐患
public void add(E e); // 增加数据
public int size(); // 获取数据的个数
public boolean isEmpty(); // 判断是否为空集合
}
在LinkImpl子类里追加判断空集合的方法:
public boolean isEmpty() {
// return null == this.root; // 方法一
return 0 == this.count; // 方法二
}
(完整代码在 课时142)
实现步骤:
在ILink接口里追加返回集合数据的方法:
interface ILink { // 设置泛型避免安全隐患
public void add(E e); // 增加数据
public int size(); // 获取数据的个数
public boolean isEmpty(); // 判断是否为空集合
public Object[] toArray(); // 将集合数据以数组的形式返回
}
在LinkImpl子类里追加两个属性:
private int foot; // 操作数组的脚标
private Object[] returnData; // 返回数据的保存
在Node类中递归获取数据:
public void toArrayNode() {
LinkImpl.this.returnData[LinkImpl.this.foot ++] = this.data;
// 如果还有下一个节点,则继续去获取
if (null != this.next) this.next.toArrayNode();
}
在LinkImpl子类里追加返回集合数据的方法:
public Object[] toArray() {
if (this.isEmpty()) return null; // 空集合则直接返回null
this.foot = 0; // 脚标清零
this.returnData = new Object[this.count]; // 根据集合长度生成返回的数组
this.root.toArrayNode(); // 利用Node类递归获取数据
return this.returnData;
}
(完整代码在 课时142)
实现步骤:
在ILink接口里追加根据索引获取数据的方法:
interface ILink { // 设置泛型避免安全隐患
public void add(E e); // 增加数据
public int size(); // 获取数据的个数
public boolean isEmpty(); // 判断是否为空集合
public Object[] toArray(); // 将集合数据以数组的形式返回
public E get(int index); // 根据索引获取数据
}
在Node类中递归查找节点:
public E getNode(int index) {
if (index == LinkImpl.this.foot ++) return this.data; // 索引相同,返回数据
else return this.next.getNode(index); // 索引不同,递归下一个节点
}
在LinkImpl子类里追加根据索引获取数据的方法:
public E get(int index) {
if (this.count <= index) return null; // 如果索引超过集合长度则返回null
this.foot = 0; // 脚标清零
return this.root.getNode(index);
}
(完整代码在 课时142)
修改指定索引的数据:public void set(int index, E data)
实现步骤:
在ILink接口里追加修改指定索引数据的方法:
interface ILink { // 设置泛型避免安全隐患
public void add(E e); // 增加数据
public int size(); // 获取数据的个数
public boolean isEmpty(); // 判断是否为空集合
public Object[] toArray(); // 将集合数据以数组的形式返回
public E get(int index); // 根据索引获取数据
public void set(int index, E data); // 修改指定索引的数据
}
在Node类中递归查找节点并修改数据:
public void setNode(int index, E data) {
if (index == LinkImpl.this.foot ++) this.data = data; // 索引相同,修改数据
else this.next.setNode(index, data); // 索引不同,递归下一个节点
}
在LinkImpl子类里追加修改指定索引数据的方法:
public void set(int index, E data) {
if (this.count <= index) return; // 如果索引超过集合长度则方法调用直接结束
this.foot = 0; // 脚标清零
this.root.setNode(index, data);
}
(完整代码在 课时142)
判断数据是否存在:public boolean contains(E data)
实现步骤:
在ILink接口里追加判断数据是否存在的方法:
interface ILink { // 设置泛型避免安全隐患
public void add(E e); // 增加数据
public int size(); // 获取数据的个数
public boolean isEmpty(); // 判断是否为空集合
public Object[] toArray(); // 将集合数据以数组的形式返回
public E get(int index); // 根据索引获取数据
public void set(int index, E data); // 修改指定索引的数据
public boolean contains(E data); // 判断数据是否存在
}
在Node类中递归判断数据:
public boolean containsNode(E data) {
if (this.data.equals(data)) return true; // 找到该数据
else {
if (null == this.next) return false; // 没有后续节点,表示找不到该数据
else return this.next.containsNode(data); // 向后递归查找
}
}
在LinkImpl子类里追加判断数据是否存在的方法:
public boolean contains(E data) {
if (null == data) return false; // 数据为null则直接返回false
return this.root.containsNode(data); // 交给Node类判断
}
(完整代码在 课时142)
数据删除:public void remove(E data)
对于集合数据的删除,需要考虑的情况:
实现步骤:
在ILink接口里追加数据删除的方法:
interface ILink { // 设置泛型避免安全隐患
public void add(E e); // 增加数据
public int size(); // 获取数据的个数
public boolean isEmpty(); // 判断是否为空集合
public Object[] toArray(); // 将集合数据以数组的形式返回
public E get(int index); // 根据索引获取数据
public void set(int index, E data); // 修改指定索引的数据
public boolean contains(E data); // 判断数据是否存在
public void remove(E data); // 删除数据
}
在Node类中递归查找节点并删除数据:
public void removeNode(Node previous, E data) {
// 找到要删除的节点,将其前一个节点指向其后一个节点
if (this.data.equals(data)) previous.next = this.next;
// 下一个节点不为空,则递归下一个节点
else if (null != this.next) this.next.removeNode(this, data);
}
在LinkImpl子类里追加数据删除的方法:
public void remove(E data) {
if (this.contains(data)) { // 判断数据是否存在
// 删除的节点为根节点
if (this.root.data.equals(data)) this.root = this.root.next;
// 删除的节点不为根节点,交由Node类删除
else this.root.next.removeNode(this.root, data);
this.count --;
}
}
(完整代码在 课时142)
清空链表:public void clean()
实现步骤:
在ILink接口里追加清空集合的方法:
interface ILink { // 设置泛型避免安全隐患
public void add(E e); // 增加数据
public int size(); // 获取数据的个数
public boolean isEmpty(); // 判断是否为空集合
public Object[] toArray(); // 将集合数据以数组的形式返回
public E get(int index); // 根据索引获取数据
public void set(int index, E data); // 修改指定索引的数据
public boolean contains(E data); // 判断数据是否存在
public void remove(E data); // 删除数据
public void clean(); // 清空集合
}
在LinkImpl子类里追加清空集合的方法:
public void clean() {
this.root = null; // 赋空根节点,后续的节点也就找不到了
this.count = 0; // 个数清零
}
单向链表类的完整代码:
interface ILink { // 设置泛型避免安全隐患
public void add(E e); // 增加数据
public int size(); // 获取数据的个数
public boolean isEmpty(); // 判断是否为空集合
public Object[] toArray(); // 将集合数据以数组的形式返回
public E get(int index); // 根据索引获取数据
public void set(int index, E data); // 修改指定索引的数据
public boolean contains(E data); // 判断数据是否存在
public void remove(E data); // 删除数据
public void clean(); // 清空集合
}
class LinkImpl implements ILink {
private class Node { // 保存节点
private E data; // 保存数据
private Node next; // 保存下一个节点
public Node(E data) {
this.data = data;
}
// 第一次调用:this = LinkImpl.root;
// 第二次调用:this = LinkImpl.root.next;
// 第三次调用:this = LinkImpl.root.next.next;
public void addNode(Node newNode) { // 保存新节点
// 当前节点的下一个节点为null,则保存当前节点为下一个节点
if (null == this.next) this.next = newNode;
// 当前节点的下一个节点不为null,则递归调用
else this.next.addNode(newNode); //
}
// 第一次调用:this = LinkImpl.root;
// 第二次调用:this = LinkImpl.root.next;
// 第三次调用:this = LinkImpl.root.next.next;
public void toArrayNode() {
LinkImpl.this.returnData[LinkImpl.this.foot ++] = this.data;
// 如果还有下一个节点,则继续去获取
if (null != this.next) this.next.toArrayNode();
}
public E getNode(int index) {
if (index == LinkImpl.this.foot ++) return this.data; // 索引相同,返回数据
else return this.next.getNode(index); // 索引不同,递归下一个节点
}
public void setNode(int index, E data) {
if (index == LinkImpl.this.foot ++) this.data = data; // 索引相同,修改数据
else this.next.setNode(index, data); // 索引不同,递归下一个节点
}
public boolean containsNode(E data) {
if (this.data.equals(data)) return true; // 找到该数据
else {
if (null == this.next) return false; // 没有后续节点,表示找不到该数据
else return this.next.containsNode(data); // 向后递归查找
}
}
public void removeNode(Node previous, E data) {
// 找到要删除的节点,将其前一个节点指向其后一个节点
if (this.data.equals(data)) previous.next = this.next;
// 下一个节点不为空,则递归下一个节点
else if (null != this.next) this.next.removeNode(this, data);
}
}
// ———————————— 以下为Link类中定义的成员 ————————————
private Node root; // 保存根节点
private int count; // 保存数据个数
private int foot; // 操作数组的脚标
private Object[] returnData; // 返回数据的保存
// ———————————— 以下为Link类中定义的方法 ————————————
public void add(E e) {
if (null == e) return; // 如增加的数据为null,则方法调用直接结束
Node newNode = new Node(e);
if (null == this.root) this.root = newNode; // 如没有根节点,则增加的节点即为根节点
else this.root.addNode(newNode);
this.count ++;
}
public int size() {
return this.count;
}
public boolean isEmpty() {
// return null == this.root; // 方法一
return 0 == this.count; // 方法二
}
public Object[] toArray() {
if (this.isEmpty()) return null; // 空集合则直接返回null
this.foot = 0; // 脚标清零
this.returnData = new Object[this.count]; // 根据集合长度生成返回的数组
this.root.toArrayNode(); // 利用Node类递归获取数据
return this.returnData;
}
public E get(int index) {
if (this.count <= index) return null; // 如果索引超过集合长度则返回null
this.foot = 0; // 脚标清零
return this.root.getNode(index);
}
public void set(int index, E data) {
if (this.count <= index) return; // 如果索引超过集合长度则方法调用直接结束
this.foot = 0; // 脚标清零
this.root.setNode(index, data);
}
public boolean contains(E data) {
if (null == data) return false; // 数据为null则直接返回false
return this.root.containsNode(data); // 交给Node类判断
}
public void remove(E data) {
if (this.contains(data)) { // 判断数据是否存在
if (this.root.data.equals(data)) this.root = this.root.next; // 删除的节点为根节点
else this.root.next.removeNode(this.root, data); // 删除的节点不为根节点,交由Node类删除
this.count --;
}
}
public void clean() {
this.root = null; // 赋空根节点,后续的节点也就找不到了
this.count = 0; // 个数清零
}
}
public class JavaDemo {
public static void main(String[] args) {
ILink link = new LinkImpl<>();
System.out.println("【数据增加之前】数据个数:" + link.size() + ",是否为空集合:" + link.isEmpty());
link.add("火车头");
link.add("车厢一");
link.add("车厢二");
link.add("车厢三");
System.out.println("【数据增加之后】数据个数:" + link.size() + ",是否为空集合:" + link.isEmpty());
link.set(0, "车厢零");
link.remove("车厢三");
Object[] result = link.toArray();
for (Object obj : result) {
System.out.println(obj);
}
System.out.println("—————— 数据获取的分割线 ——————");
System.out.println(link.get(0));
System.out.println(link.get(2));
System.out.println("—————— 数据判断的分割线 ——————");
System.out.println(link.contains("车厢五"));
System.out.println(link.contains("车厢一"));
System.out.println("【数据清空之前】数据个数:" + link.size() + ",是否为空集合:" + link.isEmpty());
link.clean();
System.out.println("【数据清空之后】数据个数:" + link.size() + ",是否为空集合:" + link.isEmpty());
}
}
实现步骤:
完整代码:
interface ILink {} // 略,请参考课时142的代码
class LinkImpl implements ILink {} // 略,请参考课时142的代码
interface Pet { // 宠物标准
public String getName(); // 获取名字
public String getColor(); // 获取颜色
}
class PetShop { // 宠物商店类
public ILink pets = new LinkImpl<>();
public void add(Pet pet) {
this.pets.add(pet);
}
public void delete(Pet pet) {
this.pets.remove(pet);
}
public Object[] all () {
return this.pets.toArray();
}
public ILink search(String keyword) {
ILink searchResult = new LinkImpl<>();
Object[] result = this.pets.toArray();
if (null != result) {
for (Object obj : result) {
if (obj instanceof Pet) {
Pet pet = (Pet) obj;
if (pet.getName().contains(keyword) || pet.getColor().contains(keyword))
searchResult.add(pet);
}
}
}
return searchResult;
}
}
class Cat implements Pet {
private String name;
private String color;
public Cat(String name, String color) {
this.name = name;
this.color = color;
}
public String getName() {
return this.name;
}
public String getColor() {
return this.color;
}
public String toString() {
return "【宠物猫】名字:" + this.name + ",颜色:" + this.color;
}
public boolean equals(Object obj) {
if (null == obj) return false;
if (!(obj instanceof Cat)) return false;
if (this == obj) return true;
Cat cat = (Cat) obj;
return this.name.equals(cat.getName()) && this.color.equals(cat.getColor());
}
}
class Dog implements Pet {
private String name;
private String color;
public Dog(String name, String color) {
this.name = name;
this.color = color;
}
public String getName() {
return this.name;
}
public String getColor() {
return this.color;
}
public String toString() {
return "【宠物狗】名字:" + this.name + ",颜色:" + this.color;
}
public boolean equals(Object obj) {
if (null == obj) return false;
if (!(obj instanceof Dog)) return false;
if (this == obj) return true;
Dog dog = (Dog) obj;
return this.name.equals(dog.getName()) && this.color.equals(dog.getColor());
}
}
public class JavaDemo {
public static void main(String[] args) {
PetShop shop = new PetShop(); // 开宠物商店
Pet pet1 = new Dog("黄斑狗", "黄色");
Pet pet2 = new Cat("小强猫", "绿色");
Pet pet3 = new Cat("小蓝猫", "蓝色");
Pet pet4 = new Cat("大黄猫", "黄色");
Pet pet5 = new Dog("大黑狗", "黑色");
shop.add(pet1); // 上架
shop.add(pet2); // 上架
shop.add(pet3); // 上架
shop.add(pet4); // 上架
shop.add(pet5); // 上架
System.out.println("—————— 上架宠物 ——————");
Object[] result = shop.all();
for (Object obj : result) {
System.out.println(obj);
}
System.out.println("—————— 查找宠物 ——————");
result = shop.search("黄").toArray(); // 查找
for (Object obj : result) {
System.out.println(obj);
}
System.out.println("—————— 上架宠物 ——————");
shop.delete(pet4); // 下架
shop.delete(pet5); // 下架
result = shop.all();
for (Object obj : result) {
System.out.println(obj);
}
}
}
interface ILink { // 设置泛型避免安全隐患
public void add(E e); // 增加数据
public int size(); // 获取数据的个数
public boolean isEmpty(); // 判断是否为空集合
public Object[] toArray(); // 将集合数据以数组的形式返回
public E get(int index); // 根据索引获取数据
public void set(int index, E data); // 修改指定索引的数据
public boolean contains(E data); // 判断数据是否存在
public void remove(E data); // 删除数据
public void clean(); // 清空集合
}
class LinkImpl implements ILink {
private class Node { // 保存节点
private E data; // 保存数据
private Node next; // 保存下一个节点
public Node(E data) {
this.data = data;
}
// 第一次调用:this = LinkImpl.root;
// 第二次调用:this = LinkImpl.root.next;
// 第三次调用:this = LinkImpl.root.next.next;
public void addNode(Node newNode) { // 保存新节点
// 当前节点的下一个节点为null,则保存当前节点为下一个节点
if (null == this.next) this.next = newNode;
// 当前节点的下一个节点不为null,则递归调用
else this.next.addNode(newNode); //
}
// 第一次调用:this = LinkImpl.root;
// 第二次调用:this = LinkImpl.root.next;
// 第三次调用:this = LinkImpl.root.next.next;
public void toArrayNode() {
LinkImpl.this.returnData[LinkImpl.this.foot ++] = this.data;
// 如果还有下一个节点,则继续去获取
if (null != this.next) this.next.toArrayNode();
}
public E getNode(int index) {
if (index == LinkImpl.this.foot ++) return this.data; // 索引相同,返回数据
else return this.next.getNode(index); // 索引不同,递归下一个节点
}
public void setNode(int index, E data) {
if (index == LinkImpl.this.foot ++) this.data = data; // 索引相同,修改数据
else this.next.setNode(index, data); // 索引不同,递归下一个节点
}
public boolean containsNode(E data) {
if (this.data.equals(data)) return true; // 找到该数据
else {
if (null == this.next) return false; // 没有后续节点,表示找不到该数据
else return this.next.containsNode(data); // 向后递归查找
}
}
public void removeNode(Node previous, E data) {
// 找到要删除的节点,将其前一个节点指向其后一个节点
if (this.data.equals(data)) previous.next = this.next;
// 下一个节点不为空,则递归下一个节点
else if (null != this.next) this.next.removeNode(this, data);
}
}
// ———————————— 以下为Link类中定义的成员 ————————————
private Node root; // 保存根节点
private int count; // 保存数据个数
private int foot; // 操作数组的脚标
private Object[] returnData; // 返回数据的保存
// ———————————— 以下为Link类中定义的方法 ————————————
public void add(E e) {
if (null == e) return; // 如增加的数据为null,则方法调用直接结束
Node newNode = new Node(e);
if (null == this.root) this.root = newNode; // 如没有根节点,则增加的节点即为根节点
else this.root.addNode(newNode);
this.count ++;
}
public int size() {
return this.count;
}
public boolean isEmpty() {
// return null == this.root; // 方法一
return 0 == this.count; // 方法二
}
public Object[] toArray() {
if (this.isEmpty()) return null; // 空集合则直接返回null
this.foot = 0; // 脚标清零
this.returnData = new Object[this.count]; // 根据集合长度生成返回的数组
this.root.toArrayNode(); // 利用Node类递归获取数据
return this.returnData;
}
public E get(int index) {
if (this.count <= index) return null; // 如果索引超过集合长度则返回null
this.foot = 0; // 脚标清零
return this.root.getNode(index);
}
public void set(int index, E data) {
if (this.count <= index) return; // 如果索引超过集合长度则方法调用直接结束
this.foot = 0; // 脚标清零
this.root.setNode(index, data);
}
public boolean contains(E data) {
if (null == data) return false; // 数据为null则直接返回false
return this.root.containsNode(data); // 交给Node类判断
}
public void remove(E data) {
if (this.contains(data)) { // 判断数据是否存在
if (this.root.data.equals(data)) this.root = this.root.next; // 删除的节点为根节点
else this.root.next.removeNode(this.root, data); // 删除的节点不为根节点,交由Node类删除
this.count --;
}
}
public void clean() {
this.root = null; // 赋空根节点,后续的节点也就找不到了
this.count = 0; // 个数清零
}
}
interface IGood { // 商品标准
public String getName(); // 获取商品名称
public double getPrice(); // 获取商品价格
}
interface IShopCar { // 购物车标准
public void add(IGood good); // 添加商品
public void delete(IGood good); // 删除商品
public Object[] all(); // 获取所有商品
}
class ShopCarImpl implements IShopCar {
private ILink goods = new LinkImpl<>();
public void add(IGood good) {
this.goods.add(good);
}
public void delete(IGood good) {
this.goods.remove(good);
}
public Object[] all() {
return this.goods.toArray();
}
}
class Cashier { // 收银台类
private IShopCar shopCar;
public Cashier(IShopCar shopCar) {
this.shopCar = shopCar;
}
public double checkOut() { // 结账,计算总价
double total = 0.0;
Object[] result = this.shopCar.all();
for (Object obj : result) {
IGood good = (IGood) obj;
total += good.getPrice();
}
return total;
}
public int getCount() { // 获取商品数量
return this.shopCar.all().length;
}
}
class Book implements IGood {
private String name;
private double price;
public Book(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return this.name;
}
public double getPrice() {
return this.price;
}
public String toString() {
return "【图书】名称:" + this.name + ",价格:" + this.price;
}
public boolean equals(Object obj) {
if (null == obj) return false;
if (this == obj) return true;
if (!(obj instanceof Book)) return false;
Book book = (Book) obj;
return this.name.equals(book.getName()) && this.price == book.getPrice();
}
}
class Bag implements IGood {
private String name;
private double price;
public Bag(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return this.name;
}
public double getPrice() {
return this.price;
}
public String toString() {
return "【背包】名称:" + this.name + ",价格:" + this.price;
}
public boolean equals(Object obj) {
if (null == obj) return false;
if (this == obj) return true;
if (!(obj instanceof Bag)) return false;
Bag bag = (Bag) obj;
return this.name.equals(bag.getName()) && this.price == bag.getPrice();
}
}
public class JavaDemo {
public static void main(String[] args) {
IShopCar shopCar = new ShopCarImpl();
IGood good1 = new Book("Java开发", 79.8);
IGood good2 = new Book("Oracle开发", 89.8);
IGood good3 = new Bag("Nike背包", 889.8);
shopCar.add(good1);
shopCar.add(good2);
shopCar.add(good3);
Cashier cashier = new Cashier(shopCar);
System.out.println("总计:" + cashier.checkOut() + ",购买件数:" + cashier.getCount());
shopCar.delete(good3);
System.out.println("总计:" + cashier.checkOut() + ",购买件数:" + cashier.getCount());
}
}
————————————————————————————————————