文章目录
- 第一章 类与对象
- 1.1 面向对象
- 面向对象三个主要特征:
- 1.2 类与对象
- 1.3 对象内存分析
- 1.4 对象引用分析
- 1.5 引用与垃圾产生分析
- 1.6.1 成员属性封装处理
- 1.6.2 **this**有三种用法:
- 1.7 构造方法与匿名对象
- 1.8 简单Java类(超重要)
- 第二章 数组
- 2.2 foreach 输出
- 2.3 **二维数组**
- 第三章 数据表与简单Java类映射转换
- 第四章 String类
- 4.1 String类简介
- 4.2 字符串比较
- 4.3 字符串常量: String类的匿名数组
- 4.4 [String类两种实例化方式]()
- 4.5 String 对象常量池
- 4.6 StringBuilder 和 StringBuffer
- 4.7 [JavaDoc 文档介绍](https://docs.oracle.com/en/java/javase/15/docs/api/index.html)
- 第五章 继承
- 5.1 继承定义
- 5.2 子类对象实例化流程
- 5.3 方法覆写
- 5.4 属性覆盖
- 5.5 final关键字
- 5.6 Annotation注解
- 第六章 多态
- 6.1 多态性的基本概念
- 6.2 instanceof 关键字
- 第七章 Object类
- 7.1 Object 类基本概念
- 7.2 获取对象信息 : toString()
- 7.3 对象比较
- 第八章 抽象类的基本概念
- 8.1 抽象类的基本概念
- **8.2 抽象类的相关说明**
- 8.3 抽象类的运用
- 第九章 包装类
- 9.1 包装类的实现原理分析
- 9.2 装箱与拆箱
- 第十章 接口的定义与使用
- 10.1 接口的基本定义
- 10.2 使用接口的定义标准
- 10.3 工厂设计模式( Factory )
- 10.4 代理设计模式( Proxy )
- 10.5 抽象类和接口的区别
- 第十一章 包
- 11.1 包的定义与使用
- 11.2 包的导入
- 11.3 静态导入(很少用)
- 第十二章 单例设计模式
- 12.1 单例设计模式
- 第十三章 异常处理
- 13.1 处理异常
- 第十四章 泛型
- 14.1 泛型定义
- 3月5日
- ArrayList 遍历的三种方法
- linklist的一些用法
- Iterator 的用法
- 3月6日
- Hashset 的一些用法
- Hashmap的一些使用方法
- object 类及其操作
- 泛型方法
- 泛型类
- 泛型函数
- Comparator用法
- lambda表达式
- 内部类
- **个人总结:**
- 封装
- 继承
- 多态: 多态就是同一个接口,使用不同的实例而执行不同操作
- 抽象方法
- [static 关键字]()
- **子类继承父类的加载顺序详解**
- 一:单独类的加载顺序
- 二:子类继承父类的加载顺序
第一章 类与对象
1.1 面向对象
面向对象三个主要特征:
- 封装性:内部的操作对外部而言不可见,当内部的操作都不可直接使用的时候才是安全的; - 继承性:在已有结构的基础上,继续进行功能的扩充; - 多态性:是在继承性的基础上扩充而来的概念,指得是类型的转换处理
1.2 类与对象
类:类是对某一类实物的共性的抽象概念。
对象:对象描述的是一个具体的产物。
两者之间关系:对象是从类中实例化出来的例子。
1.3 对象内存分析
Java之中类属于引用数据类型,引用数据类型最大的困难之处在于要进行内存的管理,同时在操作的时候也会发生有内存关系的一个变化。
- 堆内存:存储对象的具体信息(属性,方法等等)
- 栈内存:存储分配出来的内存的地址(储存的是指向内存的指针)
// 类的定义
class Person{
String name;
int age;
public void tell{
System.out.println("姓名: "+name);
System.out.println("年龄: "+age);
}
}
// 声明并实例化对象
Person p = new Person();
p.name = "张三";
p.age = 20;
// 进行方法的调用
p.tell();
// 1.new 开辟了新的堆内存, new拥有开辟内存的最高级别
// 2.开辟的内存内容是要根据Person类来进行初始化的
注意:所有的对象在调用类中的属性或方法的时候必须要**实例化**完成后才可以执行。
1.4 对象引用分析
1.4.1 引用传递分析
类本身属于引用数据类型,既然是引用数据类型,那就牵扯到内存的引用传递,所谓的引用传递的本质:同一块堆内存空间,可以被不同的栈内存指向,也可以更换指向。(可以当成C指针)
例子:
public class Demo { public static void main(String[] args){ Person p1 = new Person(); p1.name = "zhangsan"; p1.age = 20; p1.tell(); System.out.println("-----------p2------------"); Person p2 = p1; // 引用传递 p2.age = 80; p2.tell(); System.out.println("-----------p1------------"); p1.tell(); } } class Person{ String name; int age; public void tell(){ System.out.println("姓名 : "+ name); System.out.println("年龄 : "+ age); } }
Person p2 = p1;
p1 代表一个地址,比如0x0001
p2 = p1;
代表p2也等于 0x0001,指向堆空间中同一块内存
p2.age = 80;
修改了该堆空间中的数值,这时,p1本身数值没有改变仍是0x0001,也就是说,仍然指向着这块堆空间(已经被修改)所以再次调用p1.tell(); 后,输出 age也为80。输出结果:
姓名 : zhangsan 年龄 : 20 --------------------p2-------------------------- 姓名 : zhangsan 年龄 : 80 --------------------p1-------------------------- 姓名 : zhangsan 年龄 : 80
1.5 引用与垃圾产生分析
**垃圾:**没有任何栈内存指向的堆内存空间,所有的垃圾将被GC(垃圾收集器,Garbage Collector)不定期进行回收并释放无用内存空间,但是如果垃圾过多,一定影响到GC的处理性能,从而降低整体的程序性能,所以在开发过程中,垃圾产生应该越少越好。
1.6.1 成员属性封装处理
在类之中的组成就是属性和方法,一般而言方法都是对外提供服务的,所以不需要封装,而属性需要较高的安全性,往往需要对其进行保护,这时候就需要采用封装性对属性进行保护。
属性封装方法:(类中所有属性都必须使用private封装!!!)
- 修改属性的可见性来限制对属性的访问(一般限制为private),例如:
public class Person { private String name; private int age; }
这段代码中,将 name 和 age 属性设置为私有的,只能本类才能访问,其他类都访问不了,如此就对信息进行了隐藏。
- 【getter】、【setter】设置:对每个值属性提供对外的公共方法访问,也就是创建一对赋取值方法,用于对**私有属性(private)**的访问,一定要配合使用,否则外部还是能通过直接引用来修改属性,这就违背了使用setter和getter的意义
例子:
public class Demo {
public static void main(String[] args) {
Person p1 = new Person();
p1.setName("张三");
p1.setAge(19);
p1.tell();
//p1.name = "李四";
/*
这时这里已经无法直接对name属性进行修改了,
会报错:'name' 在 'com.Person' 中具有 private 访问权限
*/
}
}
class Person {
/**
* 思考:
* 虽然我们使用了getter和setter进行类的封装,但是在调用方法上,依然可以使用.name这样的
* 语句进行直接调用,有没有像python中 ._name这种形式使得在外部无法调用.name呢?
*
* 答案是有的,在Java中我们只要使用【private】权限修饰符修饰我们希望封装的对象属性即可。
*/private String name;
private int age;
/**
* 使用setter和getter方法
*/
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 void tell() {
System.out.println("姓名" + this.name);
System.out.println("年龄" + this.age);
}
}
1.6.2 this有三种用法:
- 当前类中的属性:this.属性名
使用this 调用当前类中属性
- 代码第6行和第10行:this关键字是为了解决实例变量(private String name)和局部变量(setName(String name)中的name变量)之间发生的同名的冲突。
- 只要访问本类中的属性,一定加上this进行属性访问,养成好习惯
- 当前类中的方法(普通方法、构造方法):this.方法名()、this()
- 构造方法调用(this()):使用关键字new实例化对象时才会调用构造方法
- 普通方法调用(this.方法名称()): 利用重构和this() 来提高代码重用
- 构造方法必须在实例化新对象时调用,因此this()的语句只允许放在【构造方法】的【首行】
- 构造方法互相调用时,要注意别形成死循环。
例子:
public class Demo {
public static void main(String[] args) {
Person p1 = new Person();
System.out.println("--------【使用getter、setter】--------");
System.out.println("--------【预期结果:张三 19】--------");
p1.setName("张三");
p1.setAge(19);
p1.tell();
System.out.println("--------【使用构造方法】--------");
System.out.println("--------【预期结果: 李四 28 】--------");
Person p2 = new Person("李四", 28);
p2.tell();
System.out.println("--------【使用this() 调用构造方法】--------");
System.out.println("--------【预期结果: 打印两遍“我是一个构造函数~” null 8 王五 80 】--------");
// 构造方法只能在实例初始化的时候使用
Person p3 = new Person(8);
Person p4 = new Person("王五",80);
p3.tell();
p4.tell();
}
}
class Person {
/**
* 思考:
* 虽然我们使用了getter和setter进行类的封装,但是在调用方法上,依然可以使用.name这样的
* 语句进行直接调用,有没有像python中 ._name这种方法使得在外部无法使用.name方法呢?
* <p>
* 答案是有的,在Java中我们只要使用private权限修饰符就可以了
*/
private String name;
private int age;
/**
* 使用setter和getter方法
*/
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;
}
/**
* 用this()方法,把构造函数和重载结合,减少代码重用
*/
// public Person() {
// System.out.println("我是一个构造函数~");
// }
//
// public Person(int age) {
// System.out.println("我是一个构造函数~");
// this.age = age;
// }
/**
* 观察上方两个构造函数,发现有的部分是重复的代码,为了减少重复,
* 可以使用 this() 来调用构造方法
*/
//在java类中,如果不显示声明构造函数,JVM 会给该类一个默认的构造函数。一个类可以有多个构造函数。
// 构造函数的主要作用:
// 一是用来实例化该类。
// 二是让该类实例化的时候执行哪些方法,初始化哪些属性
// 当一个类声明了构造函数以后,JVM 是不会再给该类分配默认的构造函数。
public Person() {
System.out.println("我是一个构造函数~");
}
public Person(int age) {
// 调用无参构造函数
this();
this.age = age;
}
/**
* 构造函数: 可以解决参数过多,需要设置一大堆getter、setter的问题
*/
public Person(String name, int age) {
//调用单参构造函数
this(age);
this.name = name;
}
public void tell() {
System.out.println("姓名" + this.name);
System.out.println("年龄" + this.age);
}
}
输出结果:
我是一个构造函数~
--------【使用getter、setter】--------
--------【预期结果:张三 19】--------
姓名张三
年龄19
--------【使用构造方法】--------
--------【预期结果: 李四 28 】--------
我是一个构造函数~
姓名李四
年龄28
--------【使用this() 调用构造方法】--------
--------【预期结果: 打印两遍“我是一个构造函数~” null 8 王五 80 】--------
我是一个构造函数~
我是一个构造函数~
姓名null
年龄8
姓名王五
年龄80
- 描述当前对象
.。。。。。。。。。。
1.7 构造方法与匿名对象
如果一个类中有较多的属性,那么用全部调用setter方法实在是太繁琐了,为了解决这个问题,Java中专门提供有构造方法,即,可以通过构造方法实现实例化对象中的属性初始化处理。
Java中构造方法的定义要求如下:
- 方法名称必须与类名称保持一致;
- 构造方法不允许设置任何返回值类型,也就是说,没有返回值定义。
- 构造方法是在使用关键字new实例化对象的时候自动调用的。
例子:
public class Demo {
public static void main(String[] args) {
Person p1 = new Person("", 0);
System.out.println("-------------【无封装👇】------------");
p1.age = 20;
p1.name = "zhangsan";
p1.tell();
System.out.println("-------------【构造函数👇】----------");
//构造方法是在使用关键字new实例化对象的时候自动调用的。
Person p2 = new Person("lisi", 19);
p2.tell();
}
}
class Person {
String name;
int age;
public Person(String n, int a) {
//方法名称必须与类名称保持一致;
name = n;
age = a;
}
public void tell() {
System.out.println("姓名 : " + name);
System.out.println("年龄 : " + age);
}
}
注意:
第20行处,构造方法不允许设置任何返回值类型,也就是说,没有返回值定义,也不用void!因为这里如果有类型定义,那么构造方法就与普通函数方法结构完全相同了,这样编译器会认为此方法是一个普通方法。
两者的最大区别就在于:构造方法是在类对象实例化(new的时候)的时候调用的
而普通方法是在类对象实例化完成之后调用的(new之后)输出结果:
----------------------【无封装👇】------------------------- 姓名 : zhangsan 年龄 : 20 ----------------------【构造函数👇】----------------------- 姓名 : lisi 年龄 : 19
1.8 简单Java类(超重要)
所谓的简单Java类指的是可以描述某一类信息的程序类(例如:描述一个人、描述一本书),并且在这个类中并没有特别复杂的逻辑操作。只作为一种信息存储的媒介存在。
对于简单Java类而言,其**核心开发结构**如下:
- 类名称一定要有意义,可以明确的描述某一类事物。
- 类之中所有属性都必须使用private进行封装,同时封装后的属性必须要提供有getter、setter方法。
- 类之中可以提供无数多个构造方法,但是必须要保留有无参构造方法。建议写出一个无参构造在加上一个包含所有参数的构造方法,因为如果不显示的写出无参构造方法,而写出带参的构造方法,默认的无参构造方法就会被覆盖。
- 类之中不能有任何输出语句,所有内容的获取必须返回。
- 提供一个获取对象详细信息的方法,暂时将此方法名称定义为 getInfo() ;(非必须)
例子:
/**
* 现在假设有这样一个要求,定义一个雇员类,该类会包含雇员编号、姓名、职位、基本工资几个属性信息。
*
* @author Keendy
*/
public class SimpleClass {
public static void main(String[] args) {
System.out.println(new Emp(10124563, "Keendy", "Engineer", 3300.00).getInfo());
}
}
class Emp {
private int serial;
private String name;
private String position;
private double salary;
public void setSerial(int serial) {
this.serial = serial;
}
public int getSerial() {
return this.serial;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setPosition(String position) {
this.position = position;
}
public String getPosition() {
return this.position;
}
public void setSalary(double salary) {
this.salary = salary;
}
public double getSalary() {
return this.salary;
}
/**
* 无参构造
*/
public Emp() {
}
/**
* 有参构造
*/
public Emp(int serial, String name, String position, double salary) {
this.serial = serial;
this.name = name;
this.position = position;
this.salary = salary;
}
public String getInfo() {
return "编号:" + this.serial + '\n' + "姓名:" + this.name + "\n" + "职位:" + this.position + "\n" + "薪水:" + this.salary;
}
}
第二章 数组
2.1 数组的基本概概念
数组就是一组相关变量的集合。Java中把数组定义为引用数据类型,所以数组的使用一定设计到内存的动态分配。
- 数组的动态初始化:
- 数据类型 数组名称[] = new 数据类型 [ 数组长度 ];
- 数据类型 [] 数组名称 = new 数据类型 [ 数组长度 ];
- 数组的静态初始化:在数组定义时就为其社会好了里面的内容;
简化形式:数据类型[] 数据名称 = {数据1,数据2,…,数据n};
完整格式:数据类型[] 数据名称 = new 数据类型 [] {数据1,数据2,…,数据n};
2.2 foreach 输出
这种语法的好处是可以避免下标的操作。可以类比python中的 for temp in temp_list
class ForEach{
public static void main(String [] args) {
int[] data = new int[]{1, 2, 3, 4, 5};
for (int temp: data){
System.out.println(temp);
}
// for(int i = 0;i<data.length;i++){
// System.out.println(data[i]);
// }
}
}
2.3 二维数组
如果需要多行多列的结构,就需要两个下标才可以确定一个数据,这就需要二位数组。
- 动态初始化:
type[][] typeName = new type[typeLength1][typeLength2];
- 静态初始化:
数据类型 [][] 数组名称 = new 数据类型[][]{{数据1,数据2,...数据n},{数据1,数据2,...数据n},...{}};
/**
* 二维数组的foreach方法
*/
class ForEach {
public static void main(String[] args) {
int[][] data = new int[][]{{1, 2, 3, 4, 5}, {6, 7, 8}, {9, 11, 12, 13}};
for (int[] temp : data) {
for (int num : temp) {
System.out.print(num + " ");
}
System.out.print("\n");
}
}
}
第三章 数据表与简单Java类映射转换
3.1 类的关联结构
自定义类型之间进行互相引用:
- 声明对象并设置彼此的关系
- 根据关系获取数据
个人理解:如果有相关联的属性,不要急着将其封装,因为如果是单向联系的属性并不需要进行封装,只要定义getter、setter方法就可以了。这样就可以在实例化以后通过getter、setter方法建立联系。
/**
* @author keendy
* @version 1.0
* @date 2021.02.18
* 要求通过Java程序描述出dept-emp关系,使用字段:
* <p>
* · dept:deptno、dname、loc;
* <p>
* · emp:empno、ename、job、sal、comm、deptno、mgr。
* <p>
* 在dept-emp表的关系里面存在有如下关联:
* <p>
* · 一个部门有多个雇员;
* <p>
* · 一个雇员有一个或零个领导。
* @since JDK 15.0.2
*/
class Dept {
private int deptno;
private String dname;
// 多个雇员信息
private Staff[] staff;
public Dept() {
}
public Dept(int deptno, String dname) {
this.deptno = deptno;
this.dname = dname;
this.staff = staff;
}
public int getDeptno() {
return deptno;
}
public void setDeptno(int deptno) {
this.deptno = deptno;
}
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
public Staff[] getStaff() {
return this.staff;
}
public void setStaff(Staff[] staff) {
this.staff = staff;
}
public String getInfo() {
return "部门ID: " + deptno + "\t部门名称: " + dname;
}
}
class Staff {
// 员工id
private int eno;
// 员工姓名
private String ename;
// 员工职业
private String ejob;
// 员工所属部门
private Dept dept;
// 员工领导
private Staff mgr;
public Staff() {
}
public Staff(int eno, String ename, String ejob) {
this.ejob = ejob;
this.ename = ename;
this.eno = eno;
}
// 不算属性里的getter\setter
public void setMgr(Staff mgr) {
this.mgr = mgr;
}
public Staff getMgr() {
return this.mgr;
}
//属性封装 getter\setter
public int getEno() {
return eno;
}
public void setEno(int eno) {
this.eno = this.eno;
}
public String getEname() {
return this.ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public String getEjob() {
return this.ejob;
}
public void setEjob(String ejob) {
this.ejob = ejob;
}
public Dept getDept() {
return this.dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
public String getInfo() {
return "员工编号: " + this.eno + "\t员工姓名: " + this.ename + "\t员工职业: " + this.ejob +
"\t员工部门: " + this.dept.getDname();
}
}
public class SimpleClassPractice {
public static void main(String[] args) {
// 1.声明对象并设置彼此之间的关系
// 2.根据关系获取数据
// 定义出各个实例化对象,此时并没有任何关联定义
Dept d = new Dept(2021,"制造部");
Staff s1 = new Staff(1001,"keendy","工程师A");
Staff s2 = new Staff(1002,"koko","科长");
Staff s3 = new Staff(1003,"lid","部长");
// 需要为对象进行关联的设置
s1.setDept(d);
s2.setDept(d);
s3.setDept(d);
s1.setMgr(s2);
s2.setMgr(s3);
d.setStaff(new Staff[]{s1,s2,s3});
// 部门信息
System.out.println(d.getInfo());
for(int i= 0;i<d.getStaff().length;i++){
System.out.println("\t|-"+d.getStaff()[i].getInfo());
if(d.getStaff()[i].getMgr()!=null) {
System.out.println("\t\t|-"+d.getStaff()[i].getMgr().getInfo());
}
}
System.out.println("---------------------------------------------------");
//雇员信息
System.out.println(s2.getEno()+" "+s2.getEname()+"的部门信息\n"+s2.getDept().getInfo());
}
}
3.2 数据表与简单Java类映射关系:(必须熟练掌握!)
一对一、一对多、多对多;
- 数据实体表的设计 = 类的定义
- 表中的字段 = 类的成员属性
- 表的外键关联 = 引用关联
- 表的一行记录 = 一个实例化对象
- 表的多行记录 = 对象数组
- 表与表的关系不需要设置类
第四章 String类
4.1 String类简介
String这个类里面之所以可以保存字符串主要的原因是其中定义的字符串的每一个字符数据都是保存在了字节数组(以前是字符)之中。
/**
* Constructs a new {@code String} by decoding the specified array of
* bytes using the specified {@linkplain java.nio.charset.Charset charset}.
* The length of the new {@code String} is a function of the charset, and
* hence may not be equal to the length of the byte array.
*
* <p> This method always replaces malformed-input and unmappable-character
* sequences with this charset's default replacement string. The {@link
* java.nio.charset.CharsetDecoder} class should be used when more control
* over the decoding process is required.
*
* @param bytes
* The bytes to be decoded into characters
*
* @param charset
* The {@linkplain java.nio.charset.Charset charset} to be used to
* decode the {@code bytes}
*
* @since 1.6
*/
public String(byte bytes[], Charset charset) {
this(bytes, 0, bytes.length, charset);
}
4.2 字符串比较
首先明确一个问题,String见可以使用 == 方法,但是结果不一定准确,想实现准确的String类之间的相等判断,可以使用String类的中的所提供的一个方法:
- 字符串比较:public boolean equals(String str);
public class StringClass
{
public static void main(String[] args) {
String strA = "AAA";
String strB = new String("AAA");
System.out.println(strA.equals(strB));
}
}
// 结果: true
- 思考:String比较中,“==”与equals()区别?
- “==”:进行的是数值比较,如果用于比较的对象的内存地址一样,那么就返回true
- equals():是类所提供的一个比较方法,可以直接进行字符串内容的判断
4.3 字符串常量: String类的匿名数组
f对于字符串而言,他不是基本数据类型,而是一个类,所以任何以双引号定义的字符串常量实际上描述的都是一个String类的匿名对象
public class StringClass
{
public static void main(String[] args) {
String strA = "AAA";
String strB = new String("AAA");
System.out.println(strA.equals(strB));
// 这里一个字符串常量却可以使用equals方法,说明其本质和strA一样,都是String类的对象。
System.out.println("AAA".equals(strB));
}
}
// 结果:
// true
// true
4.4 String类两种实例化方式
- 分析直接赋值的实例化模式:
- 程序值需要将一个字符串赋值给String类的对象就可以实现对象的实例化处理。
- Java程序底层里面提供有一个专门的字符串池(字符串数组)。在采用直接赋值的处理过程中,对于字符串而言可以实现吃数据的自动保存,这样如果再有相同数据定义时可以减少对象的产生以提升操作性能。(说白了就是,如果在前面已经定义了,之后如果再实例化一个内容相同的新对象,那么它的栈内存中地址保存的是原来已定义好的堆空间的地址。)
- 直接赋值的特点是可以自动将对象保存到对象池之中
public class StringClass
{
public static void main(String[] args) {
String strA = "AAA";
}
}
- 分析构造方法实例化
- 构造方法实例化对象时不会自动保存到对象池中,而是自己专门开辟一个专用的堆空间,但是String类中也存在有手工入池的方法: public String intern();
public class StringClass
{
public static void main(String[] args) {
String strA = new String("AAA");
}
}
思考: String类两种对象实例化方式的区别?
- 直接赋值:只会产生一个实例化对象,并且可以自动保存到对象池中,已实现该字符串实例的重用
- 构造方法:会产生两个实例化对象,并且不会自动入池,无法实现对象的重用
4.5 String 对象常量池
- 对象池的主要目的是实现数据的公公想处理,以String对象池为例,里面的内容主要是为了重用。
- Java中的对象池实际上可以分为两种:
- 静态常量池: 是程序(*.class)在加载时,会自动将此程序中保存的字符串\普通的常量\类和方法的信息等等,全部进行分配;
public class StringClass
{
public static void main(String[] args) {
String strA ="www.aaa.com";
String strB = "www"+".aaa."+"com";
System.out.println(strA == strB);
}
}
// 结果 : true
// 给出的内容全部都是静态常量数据,(字符串常量都是匿名对象),所以程序会先自动处理连接,然后发现
// strA已经定义了相同的对象,这就是静态常量池
- 运行时常量池: 当一个程序(*.class)加载之后,里面有一些变量,这时候提供的一个常量池;
public class StringClass
{
public static void main(String[] args) {
String strA ="www.aaa.com";
String info =".aaa.";
String strB = "www"+info+"com";
System.out.println(strA == strB);
}
}
// 结果 : false
// 在程序加载时,并不确定info的值,它是一个变量,所以存入运行时常量池
Java中的主方法组成分析 : public static void main(String[] args);
- public : 描述的是一种访问权限,主方法是一切的开始点,开始点一定是公共的
- static : 程序的执行是通过类名称完成的,所以表示此方法是由类直接调用的
- void : 主方法是一切的起点,起点一旦开始就没有返回的可能
- main : 是一个系统固定好的方法名称
- String [] args : 字符串数组,可以实现程序启动参数的接收
4.6 StringBuilder 和 StringBuffer
当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。
和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
在使用 StringBuffer 类时,每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,所以如果需要对字符串进行修改推荐使用 StringBuffer。
StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。
由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。
public class Test{
public static void main(String args[]){
StringBuilder sb = new StringBuilder(10);
sb.append("Runoob..");
System.out.println(sb);
sb.append("!");
System.out.println(sb);
sb.insert(8, "Java");
System.out.println(sb);
sb.delete(5,8);
System.out.println(sb);
}
}
4.7 JavaDoc 文档介绍
在实际项目开发过程中,只要是项目就一定会有String的定义,所以掌握这个类中的所有的常用的方法非常重要。
在以后开发中要大量使用Java的API文档,一定要用英文原始文档。(百度:JavaDoc 15)
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 | 普通 | 编码转换 |
5 | public boolean equals(String anObject ) | 普通 | 区分到小写的相等的判断 |
6 | public boolean equalsIgnoreCase(String anotherString) | 普通 | 不区分到小写的相等的判断 |
7 | public compareTo(String ) | 普通 | 返回一个int类型,返回第一组不同的字符的差值,相等返回0 |
8 | public boolean contains(CharSequence s) | 普通 | 判断字符是否存在 |
9 | public int indexOf(String str) | 普通 | 从头查找指定字符串的位置 |
NO | 方法名称 | 类型 | 描述 |
10 | public int indexOf(String str, int fromIndex) | 普通 | 从指定位置查找指定字符串的位置 |
11 | public int lastIndexOf(String str) | 普通 | 由后向前查找指定字符串的位置 |
12 | public int lastIndexOf(int ch, int fromIndex) | 普通 | 从后向前查找指定字符串的位置 |
13 | public boolean startsWith(String prefix) | 普通 | 判断是否以指定的字符串开头 |
14 | public boolean startsWith(String prefix, int toffset) | 普通 | 有指定位置判断是否以指定的字符串开头 |
15 | public boolean endsWith(String suffix) | 普通 | 是否以指定字符串结尾 |
16 | public String replace(char oldChar, char newChar) | 普通 | 替换字符 |
17 | 普通 | 按照正则的规则,替换字符串 |
NO | 方法名称 | 类型 | 描述 |
18 | 普通 | 按照正则,将字符串拆分 | |
19 | 普通 | 按照正则,将字符串拆分为指定个数 | |
20 | public String substring(int beginIndex) | 普通 | 从指定位置开始截取字符串 |
21 | public String substring(int beginIndex, int endIndex) | 普通 | 截取指定区域的字符串 |
22 | 普通 | ||
public class StringClass {
public static void main(String[] args){
String str = "ABCDEFG";
System.out.println("AAA".compareTo(str));
System.out.println(str.indexOf("BC1D"));
System.out.println(str.lastIndexOf("BCD"));
System.out.println(str.startsWith("BCD"));
System.out.println(str.startsWith("BCD",1));
System.out.println(str.endsWith("G"));
}
}
// -1
// -1
// 3
// false
// true
// true
第五章 继承
5.1 继承定义
继承的意义在于可以重用父类中的结构,并且也可以实现功能的扩充。
5.2 子类对象实例化流程
- super的作用和this差不多. 它是区分当前类和父类中重名的问题.
public class Hero {
public String name = "Hero";
}
写个孙悟空
public class SunWukong extends Hero {
public String name = "孙猴子";
public void chiTaoZi(){
System.out.println(this.name);
System.out.println(super.name);
}
}
此时在Sunwukong里就会有两个name. 默认用的是孙悟空的, 但是我想用hero的, 怎么办.?
public class Test {
public static void main(String[] args) {
SunWukong swk = new SunWukong();
swk.chiTaoZi();
}
}
- 在子类实例化的过程中,及时没有进行父类对象的实例化,也会有系统自动调用父类构造方法(实例化父类对象),默认情况下子类对象实例化流程里会自动实例化父类对象。实际上这个时候就相当于与子类的构造方法里面隐藏了一个”super();”的形式。
public class Hero {
public String name = "Hero";
// 父类的构造
public Hero(){
System.out.println("我是父类的构造");
}
}
public class SunWukong extends Hero {
public String name = "孙猴子";
// 子类的构造
public SunWukong(){
System.out.println("我是子类的构造");
}
}
public class Test {
public static void main(String[] args) {
SunWukong swk = new SunWukong();
}
}
打印结果
我是父类的构造 //先加载父类
我是子类的构造 //后加载子类
```
- “super()”表示的就是子类构造调用父类构造的语句,该语句只允许放在子类构造方法的首行,在默认情况下的实例化处理只会调用父类中的无参构造方法,所以写不写”super()” 意义并不大,但是如果父类中没有提供无参构造,这个时候就必须调用super调用有参构造。
public class Demo1 {
public static void main(String[] args) {
Father father = new Father("老王");
Son son = new Son("小王");
father.beatSon();
son.getBeat();
}
}
class Father{
public String name;
public Father(String name){
this.name = name;
}
public void beatSon(){
System.out.println("我是"+this.name+",可以打儿子");
}
}
class Son extends Father{
public String name;
public Son(String name){
super("老王");
this.name = name;
}
public void getBeat(){
System.out.println("我是"+super.name+"的儿子"+this.name+",我又挨打了555");
}
}
// 切记, 父类如果没有无参数的构造. 子类必须手动使用super调用父类的构造方法.
// 来完成父类对象的创建.
- Java中不允许多重继承,但是允许多层继承。
- 子类的父类有且仅有一个
- 可以传好几层,理论不超过三层
5.3 方法覆写
- 覆写: 子类对父类给的方法不满意. 对父类中的方法进行重新定义,覆写的意义在于提升父类的功能
- 要求: 子类方法的声明必须和父类中的方法完全一致.
public class remake {
public static void main(String[] args) {
DataBaseChannel channel = new DataBaseChannel();
channel.connect();
}
}
class Channel {
public void connect() {
System.out.println("[channel]进行资源的连接");
}
}
class DataBaseChannel extends Channel {
@Override
//覆写要做好备注 @override
public void connect(){
System.out.println("[子类database]进行连接");
}
}
// 结果 : [子类database]进行连接
- 方法覆写限制: public > default > private
- private 除了定义在属性上也可以定义在方法上
- 父类的方法权限一定要低于子类,否则不可见
- 以后开发只要是定义方法,95%全部用public
- 思考: Override (覆写)和 Overloading(重载)区别?
区别 | Overloading | Override |
中文含义 | 重载 | 覆写 |
概念 | 方法名称相同.参数的类型及个数不同(参数列表不同) | 全部与父类相同 |
权限 | 没有权限限制 | 父类权限不能高于子类 |
范围 | 在一个类中 | 不在同一类中 |
5.4 属性覆盖
当子类定义了与父类相同名称的属性名称的时候,就叫做属性的覆盖。
class Channel{
String info = "keendy";
}
class DatabaseChannel extends Channel {
String info = "Hello keendy";
public void func(){
System.out.println(this.info);
System.out.println(this.info);
}
}
public class JavaDemo{
public static void main(String[] args){
DatabaseChannel channel = new DatabaseChannel();
channel.fun;
}
}
如果属性被封装了,这个时候子类实际上就和父类中的私有属性没有关系了,即使名称一样,也是子类新定义的属性
思考: super与this的区别?
- this 表示先从本类查找所需要的属性和方法,如果本类没有,则查找父类
- super 是不查找子类直接查找父类
- this与super都可以对构造方法进行构造,但是this调用的是本类的构造,而super是子类调用父类的构造,
他们都必须放在构造方法的首行,所以不能同时出现 - this可以表示当前对象,super没有这种用法
5.5 final关键字
final关键字在程序中描述终结器的概念,它的功能为: 定义不能被继承的类,方法或常量。(类似c中的const)
5.6 Annotation注解
@override : 明确表示该方法是一个覆写来的方法
@Deprecated : 不再建议使用的方法,告诉新用户这个方法不要再用了,老用户使用不受影响
@SuppressWarnings : 发现正在进行程序编译的时候会出现一个错误的信息,一直警报,可以屏蔽警报,但是只是不再显示,不能真正消除警报。
第六章 多态
6.1 多态性的基本概念
- 方法的多态性
- 方法的重载: 同一个方法名称,根据输入参数的不同,完成不同的功能。
- 方法的覆写: 子类对父类的方法的改进。(如果是对构造方法覆写,注意第一行加super) - 对象的多态性:父子实例之间的转换处理,他有两种模式:
- 对象向上转型:父类 父类实例 = 子类实例、自动完成转换;(大部分情况)
- 对象向下转型: 子类 子类实例 = (子类)父类实例、强制完成转换;(极少使用)
例子:观察向上转型
package com.company;
public class Main {
public static void main(String[] args) {
Msg d = new Database();
// Database d = new Database();
d.print();
// 结果都是: 这是oracle,正在运行...
// 关键是看new后面的和是否进行了覆写
}
}
class Msg {
public void print() {
System.out.println("这是父类,正在运行...");
}
}
class Database extends Msg {
public void print() {
System.out.println("这是oracle,正在运行...");
}
}
//向上转型的特点是可以对参数统一进行设计,虽然用重载也可以完成相同的效果,但是考虑到可维护性,还是考虑向上转型。
6.2 instanceof 关键字
对象 instanceof 类
该判断返回一个boolean类型,如果是true表示实例是指定类对象。
例子:
class Person {
public String print(){
return "我会吃饭"
}
}
class Superman{
public String print(){
return "我会飞"
}
public String fire(){
return "我能喷火!"
}
}
public class JavaDemo{
public static void main(String[] args){
Person per = new Person();
System.out.println(per instanceof Person); // true
System.out.println(per instanceof Superman);// false
}
}
第七章 Object类
7.1 Object 类基本概念
Java中只有一个类是不存在有继承关系的,这个类就是Object类,也就是说所有的类默认情况下都是Object的子类,以下两种类的定义效果完全相同
class Person{} //一个Person类
class Person extends Object{} //一个Person类
既然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 向下转型");
}
}
}
// 输出结果:
// Person 向下转型
如果一个程序的方法要求可以接受所有类对象的时候就可以利用Object类来处理,但是要注意,在Java程序设计之中对于所有的引用数据类型实际上都可以进行接受,包括数组也可以;
例子:
public class JavaDemo{
public static void main(String[] args ){
Object obj = new int[]{1,2,3};
if(obj instanceof int[]){
int [] data = (int[]) obj;
System.out.println("Object类可以接收数组~");
for(int num : data){
System.out.print(num+" ");
}
}
}
}
// 运行结果:
// Object类可以接收数组~
// 1 2 3
7.2 获取对象信息 : toString()
Object虽然是一个类,但是这个类本身也是提供有一些处理方法的,在Object类中的"toSrting()"的方法,该方法可以获取一个对象的完整信息;
例子:
class Person{} //一个类
public class JavaDemo{
public static void main(String[] args ){
Person per = new Person();
System.out.println(per);
System.out.println(per.toString()); // 调用toString方法
}
}
// 运行结果:
com.company.Person@23fc625e
com.company.Person@23fc625e
可以发现在之前对性对象直接输出的方法就是toString方法,所以这个方法是默认的,所以在以后开发中对象信息的获得可以直接覆写此方法,以后简单java类中的getInfo()就可以直接写成toString()了
7.3 对象比较
Object类之中的另外外一个比较重要的方法就是在于对象比较的处理上,所谓的对象比较主要的功能是比较两个对象的内容是否完全相同。
// 常规方法
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Name = " + this.name + "\tAge = " + this.age;
}
} //一个类
/**
* @author wangdezhao
*/
public class Main {
public static void main(String[] args) {
Person per1 = new Person("张三", 20);
Person per2 = new Person("张三", 20);
if(per1.getName().equals(per2.getName())&& per1.getAge() == per2.getAge()){
System.out.println("per1 和 per2 相等");
}else{
System.out.println("per1 和 per2 不相等");
}
}
}
// 运行结果:
per1 和 per2 相等
这种判断方法需要每个属性都进行判断,太麻烦了
// public boolean equals(Object obj);
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Name = " + this.name + "\tAge = " + this.age;
}
// equals 方法此时会有两个对象: 当前的this,和传入的object
public boolean equals(Object obj){
Person per = (Person) obj; // 目的是获取类中的属性
return this.name.equals(per.name) && this.age == per.age;
}
} //一个类
/**
* @author wangdezhao
*/
public class Main {
public static void main(String[] args) {
Person per1 = new Person("张三", 20);
Person per2 = new Person("张三", 20);
if(per1.getName().equals(per2.getName())&& per1.getAge() == per2.getAge()){
System.out.println("per1 和 per2 相等");
}else{
System.out.println("per1 和 per2 不相等");
}
}
}
第八章 抽象类的基本概念
类继承的主要作用为扩充已有的类的功能,子类可以根据自己的选择任意来覆写某一个方法,这个时候父类无法对子类作出强制性规定(强制覆写某些方法),实际开发过程中很少继承一个完善的类(一个可以直接使用的类),而是必须继承抽象类,在以后进行父类 的设计时,要优先考虑抽象类;
8.1 抽象类的基本概念
抽象类的主要作用为,对子类中的复写方法进行约定,在抽象类中可以去定义一些抽象方法以实现这样的约定,抽象方法是使用了abstract关键字定义的并且没有提供方法体的方法,而抽象方法所在的类必须为抽象类,抽象类必须使用abstract关键字进行定义(在普通类基础上追加抽象方法就是抽象类)
例子: 抽象类定义
abstract class Msg{
private String type;
// 定义一个抽象类,抽象类不加大括号!
public abstract String getConnect();
// 普通方法
public void setType(String type){
this.type = type;
}
public String getType(){
return this.type;
}
}
当一个抽象类定义完成之后,并不是一个完整的类,使用抽象类的原则:
- 抽象类必须提供有子类,子类使用extends继承一个抽象类;
- 抽象类的子类一定要覆写抽象类中的全部的抽象方法;
- 抽象类的对象实例化可以利用对象多态性通过子类向上转型的方式完成;
例子: 使用抽象类
abstract class Msg{
private String type;
public abstract String getConnect();
// 普通方法
public void setType(String type){
this.type = type;
}
public String getType(){
return this.type;
}
}
/*子类继承抽象类*/
class DatabaseMsg extends Msg{
//覆写所有的抽象方法
@Override
public String getConnect(){
return "oracle 数据库正在连接...";
}
}
public class Abstract {
public static void main(String[] args){
Msg m = new DatabaseMsg();
System.out.println(m.getConnect());
m.setType("from Apple客户端");
System.out.println(m.getType());
}
}
/*
运行结果 :
oracle 数据库正在连接...
from Apple客户端
*/
从整体上来讲,抽象类只是比普通类增加了抽象方法以及对子类的强制性的覆写要求,其他过程和传统的类继承过程完全相同的;
抽象类使用的注意事项:
1. 抽象类使用很大程度上有一个核心问题: **抽象类无法自己直接实例化**
2. 抽象类之中的主要目的是进行过度操作使用,所以当使用抽象类进行开发时,往往是在设计中需要解决类继承问题时所带来的代码重复处理。
8.2 抽象类的相关说明
- 在定义抽象类的时候绝对不能使用final关键字来定义,因为抽象类必须有子类,而final定义的类是不可以有子类的。
- 抽象类是作为一个普通类的加强版出现的(抽象类的组成是在普通类的基础上扩展而来的,只是追加了抽象方法),那么普通类中就可以定义属性和方法,这些属性一定是要进行内存空间开辟的,所以抽象类一定可以提供有构造方法,并且子类也一定会按照子类对象的实例化原则进行父构造的调用。
abstract class Msg{
private String type;
// 增加构造方法
public Msg(String type){
this.type = type;
}
public abstract String getConnect();
// 普通方法
public void setType(String type){
this.type = type;
}
public String getType(){
return this.type;
}
}
class DatabaseMsg extends Msg{
// 子类构造方法必须把super()写在第一行
public DatabaseMsg(String str){
super(str);
}
@Override
public String getConnect(){
return "oracle 数据库正在连接...";
}
}
public class Abstract {
public static void main(String[] args){
Msg m = new DatabaseMsg("from Apple客户端");
System.out.println(m.getConnect());
System.out.println(m.getType());
}
}
/*
运行结果 :
oracle 数据库正在连接...
from Apple客户端
*/
- 抽象类中允许没有抽象方法,但即便如此,也不能直接用new实例化一个对象
- abstract类和普通的类一样,也由属性和方法组成,但是其方法在声明时只写部分,子类实例化时,只需要对抽象类中的方法进行覆写即可(注意写@Override),其余和继承普通类的方法一样。
8.3 抽象类的运用
- 现在要描述三类的事物:
- 机器人: 不休息,只知道补充能量和工作
- 人: 需要休息,需要吃饭和努力的工作
- 猪: 需要休息,需要吃饭不需要工作
abstract class Action {
public static final int SLEEP = 1;
public static final int EAT = 5;
public static final int WORK = 10;
public abstract void sleep();
public abstract void work();
public abstract void eat();
public void command(int code) {
switch (code) {
case SLEEP:
this.sleep();
break;
case EAT:
this.eat();
break;
case WORK:
this.work();
break;
default:
break;
}
}
}
class Robot extends Action {
@Override
public void eat() {
System.out.println("机器人要充电");
}
@Override
public void sleep() {
System.out.println("机器人不需要休息");
}
@Override
public void work() {
System.out.println("机器人一天工作24h");
}
}
class Human extends Action {
@Override
public void eat() {
System.out.println("人要吃美食");
}
@Override
public void sleep() {
System.out.println("人需要好好休息");
}
@Override
public void work() {
System.out.println("人的工作是996");
}
}
class Pig extends Action {
@Override
public void eat() {
System.out.println("猪吃剩饭");
}
@Override
public void sleep() {
System.out.println("猪天天休息");
}
@Override
public void work() {
System.out.println("猪不需要工作");
}
}
public class Abstract {
public static void main(String[] args) {
// 采用向上转型的方法来实现实例对象的声明
Action robot = new Robot();
Action human = new Human();
Action pig = new Pig();
System.out.println("----------------机器人-----------------");
robot.command(Action.SLEEP);
System.out.println("----------------人-----------------");
human.command(Action.SLEEP);
System.out.println("----------------猪猪-----------------");
pig.command(Action.SLEEP);
}
}
/*
----------------机器人-----------------
机器人不需要休息
----------------人-----------------
人需要好好休息
----------------猪猪-----------------
猪天天休息
*/
第九章 包装类
9.1 包装类的实现原理分析
- 包装类的主要功能是针对基本数据类型的对象装换而实现的
- Object类的最大特点是左右类的父类,可以接收所有的数据类型,但是有个问题就是: 基本数据类型并不是一个类,如果想要将基本数据类型以类的形式进行处理,那么久需要对其进行包装。
例子:包装处理int类型
// 把int包装起来
class Int{
private int data;
public Int(int data){
this.data = data;
}
public int intValue(){
return this.data;
}
}
public class IntPackage {
public static void main(String[] args){
// 装箱 : 将基本数据类型保存在包装中
Object i = new Int(10);
// 拆箱 : 从包装类对象中获取基本数据类型
int x = ((Int)i).intValue();
System.out.println(x*2);
}
}
// 运行结果 :
// 20
Java 中一共有两种包装类:
- 对象性包装类(Object直接子类):Boolean、Character
- 数值型包装类(Number直接子类(Abstract类)):Byte、Short、Integer、Long、Float、Double
Number 抽象类一共有六个方法:
No | 方法名称 | 类型 | 描述 |
01 | public byte byteValue() | 普通 | 从包装类中获取byte类型 |
02 | public short shortValue() | 普通 | 从包装类中获取short类型 |
03 | public int intValue() | 普通 | 从包装类中获取int类型 |
04 | public abstract long longValue() | 普通 | 从包装类中获取long类型 |
05 | public abstract float floatValue() | 普通 | 从包装类中获取float类型 |
06 | public abstract double doubleValue() | 普通 | 从包装类中获取double类型 |
9.2 装箱与拆箱
- 数据装箱 : 将基本数据类型保存在包装中,一般可以利用构造方法完成。
- 数据拆箱 : 从包装类对象中获取基本数据类型。
- JDK9以后,已经可以自动拆箱\装箱了
第十章 接口的定义与使用
10.1 接口的基本定义
抽象类与普通类相比最大的优势在于:可以实现对子类的覆写方法的控制,但是在抽象类里面仍然可能保留一些有普通的方法,而普通方法里面可能会涉及一些安全或者隐私的操作问题,那么这样在进行开发的过程之中,如果**想要对外部隐藏全部的细节,则可以通过接口进行描述。**
接口可以理解为一个纯粹的抽象类(最原始的定义接口之中是只包含有抽象方法与全局常量的),但是从1.8版本开始由于引入了Lamda表达式的概念,所以接口的定义也得到了加强,除了抽象的方法与全局常量之外,还可以定义普通方法或者静态方法。如果从设计本身的角度来讲,接口之中的组成还应该以抽象方法与全局常量为主。
在Java中接口主要以interface关键字来进行定义。
例子:定义一个接口
// 由于类名称与接口名称的定义要求相同,所以为了区分,要在开头加上大写'I'
interface IMessage{
// 全局常量
public static final String INFO = "www.baidu.com";
// 抽象方法
public abstract String getInfo();
}
但是现在很明显的问题出现了,此时的接口肯定无法直接产生实例化对象,所以对于接口的使用原则如下:
- 接口需要被子类实现(implements),一个子类可以实现多个接口
- 子类(如果不是抽象类),那么一定要覆写接口中全部的抽象方法
- 接口对象可以利用子类对象的向上转型进行实例化
接口有以下特性:
- 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
- 接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。
- 接口中的方法都是公有的。
接口与类的相似点:
- 一个接口可以有多个方法。
- 接口文件保存在 .java 结尾的文件中,文件名使用接口名。
- 接口的字节码文件保存在 .class 结尾的文件中。
- 接口相应的字节码文件必须在与包名称相匹配的目录结构中。
接口与类的区别:
- 接口不能用于实例化对象。
- 接口没有构造方法。
- 接口中所有的方法必须是抽象方法。
- 接口不能包含成员变量,除了 static 和 final 变量。
- 接口不是被类继承了,而是要被类实现。
- 接口支持多继承。
例子: 定义接口
public class JavaDemo {
public static void main(String[] args) {
IMessage msg = new MessageImpl();
System.out.println(msg.getInfo());
System.out.println(IMessage.INFO);
}
}
// 由于类名称与接口名称的定义要求相同,所以为了区分,要在开头加上大写'I'
interface IMessage {
// 全局常量
public static final String INFO = "keendy";
// 抽象方法
public abstract String getInfo();
}
// Impl 是 implements 的缩写,加在后面与普通类区别
class MessageImpl implements IMessage {
@Override
public String getInfo() {
return "123";
}
}
/*
运行结果:
123
keendy
*/
在Java中之所以使用接口的主要目的是一个子类可以实现多个接口,也就是说利用接口可以实现多继承的概念。
例子: 观察子类实现多个父接口
public class JavaDemo {
public static void main(String[] args) {
IMessage msg = new MessageImpl();
System.out.println(msg.getInfo());
}
}
// 由于类名称与接口名称的定义要求相同,所以为了区分,要在开头加上大写'I'
interface IMessage {
// 全局常量
public static final String INFO = "www.baidu.com";
// 抽象方法
public abstract String getInfo();
}
interface IChannel {
// 定义抽象方法
public abstract boolean connect();
}
// Impl 是 implements 的缩写,加在后面与普通类区别
class MessageImpl implements IMessage,IChannel{
@Override
public String getInfo() {
/*this表示MessageImpl对象*/
if (this.connect()) {
return "得到一个消息,123123";
}
return "获取消息失败";
}
@Override
public boolean connect() {
System.out.println("消息在channel中传输成功...");
return true;
}
}
这时候我们需要考虑对象的转型问题:
public class JavaDemo {
public static void main(String[] args) {
IMessage msg = new MessageImpl();
// 这里两者是可以相互转化的
Ichannel chl =(IChannel) msg;
System.out.println(chl.getInfo());
}
}
// 由于类名称与接口名称的定义要求相同,所以为了区分,要在开头加上大写'I'
interface IMessage {
// 全局常量
public static final String INFO = "www.baidu.com";
// 抽象方法
public abstract String getInfo();
}
interface IChannel {
// 定义抽象方法
public abstract boolean connect();
}
// Impl 是 implements 的缩写,加在后面与普通类区别
class MessageImpl implements IMessage,IChannel{
@Override
public String getInfo() {
/*this表示MessageImpl对象*/
if (this.connect()) {
return "得到一个消息,123123";
}
return "获取消息失败";
}
@Override
public boolean connect() {
System.out.println("消息在channel中传输成功...");
return true;
}
}
由于MessageImpl 实现了IMessage和IChannel的两个接口,所以这个子类可以是这两个接口任意一个接口的实例那么就表示这两个接口实例之间是可以相互转化的,但仅仅局限于通过相同的类去实例化的情况。
Object 类可以接收所有数据类型,包括基本数据类型、类对象、数组
由于接口描述的是一个公共的定义标准,所以在接口之中所有的抽象方法的访问权限都为public,也就是说,写与不写都是很一样的。
例如下面两个接口的本质是完全相同的。
完整定义:
interface IMessage{
public static final String INFO = "www.baidu.com ";
public abstract String getInfo();
}
简化定义:
interface IMessage{
String INFO = "www.baidu.com";
String getInfo();
}
一个抽象类可以实现多个接口,一个普通类只能继承一个抽象类并且可以实现多个父接口,但是要求先继承后实现:
例子:
public class JavaDemo{
public static void main(String[] args){
IMessage msg = new MessageImpl();
IChannel chl =(IChannel) msg;
System.out.println(msg.getInfo());
}
}
// 由于类名称与接口名称的定义要求相同,所以为了区分,要在开头加上大写'I'
interface IMessage{
// 全局常量
public static final String INFO = "www.baidu.com";
// 抽象方法
public abstract String getInfo();
}
interface IChannel{
// 定义抽象方法
public abstract boolean connect();
}
// Impl 是 implements 的缩写,加在后面与普通类区别
// 先继承再实现
class MessageImpl extends DatabaseAbstract implements IChannel,IMessage{
@Override
public String getInfo(){
if(this.connect()){
return "数据库中得到一个消息,123123";
}else{
return "数据库连接失败...";
}
}
@Override
public boolean connect(){
System.out.println("消息在channel中");
return true;
}
@Override
public boolean getDatabaseConnection(){
return true;
}
}
abstract class DatabaseAbstract{
// 接口中abstract可以省略,但是抽象类中不可以省略
public abstract boolean getDatabaseConnection();
}
在实际的开发中,接口的使用主要有三种形式:
- 进行标准设置
- 表达一种操作的能力
- 暴露远程方法视图,这个一般都在RCP分布式开发中使用
10.2 使用接口的定义标准
个人理解: 使用接口来定义一种标准,这种标准可以成为连接两类事物的桥梁,符合就连接,不符合就断开。
interface IUSB{
public boolean check();
public void work();
}
class Computer {
public void plugin(IUSB usb){
if(usb.check()){
usb.work();
}else{
System.out.println("硬件设备安装失败...");
}
}
}
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());
}
}
10.3 工厂设计模式( Factory )
对于接口而言,已经可以明确的清楚,必须通过子类,并且子类可以通过对象向上转型来获取接口的实例化对象。使用工厂设计模式完全隐藏了子类
interface IFood {
public 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 {
public 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 Bread();
food.eat();
// 这里对主类进行了修改,但实际上面包牛奶都是食物接口,主类不应该改变,这就出现了耦合
IFood milk = new Milk();
milk.eat();
}
}
造成耦合最直接的元凶就是: “关键字new”。以JVM的设计为例,JVM的核心原理就是运用虚拟机来运行程序,左右的程序并不与操作系统有任何的关联,而是由JVM来进行匹配,所以得出的结论就是,良好的设计应该避免耦合。
这时就需要工厂设计模式,在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
import java.util.Scanner;
/**
* @author wangdezhao
*/
interface IFood {
public 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 Milk();
} else {
return null;
}
}
}
/**
* @author wangdezhao
*/
public class JavaDemo {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
IFood food = Factory.getInstance(s);
food.eat();
}
}
10.4 代理设计模式( Proxy )
Java 的代理就是客户类不再直接和委托类打交道, 而是通过一个中间层来访问, 这个中间层就是代理。为啥要这样呢, 是因为使用代理有 2 个优势:
- 可以隐藏委托类的实现
- 可以实现客户与委托类之间的解耦, 在不修改委托类代码的情况下能够做一些额外的处理
package com.company;
// Java 静态代理
// 委托接口
public interface IHelloService {
/**
* 定义接口方法
* @param userName
* @return
*/
String sayHello(String userName);
}
// 委托类实现
public class HelloService implements IHelloService {
@Override
public String sayHello(String userName) {
System.out.println("helloService" + userName);
return "HelloService" + userName;
}
}
// 代理类
public class StaticProxyHello implements IHelloService {
// 具有相同父接口的类之间可以相互转化
private IHelloService helloService = new HelloService();
@Override
public String sayHello(String userName) {
/** 代理对象可以在此处包装一下*/
System.out.println("代理对象包装礼盒...");
return helloService.sayHello(userName);
}
}
// 测试静态代理类
public class MainStatic {
public static void main(String[] args) {
StaticProxyHello staticProxyHello = new StaticProxyHello();
staticProxyHello.sayHello("isole");
}
}
10.5 抽象类和接口的区别
NO | 区别 | 抽象类 | 接口 |
1 | 定义关键字 | abstract class 抽象类名称{} | Intterface 接口名称{} |
2 | 组成 | 构造\普通方法\静态方法\全局常量\成员 | 抽象方法\全局常量\普通方法\static方法 |
3 | 权限 | 可以使用各种权限定义 | 只能使用public |
4 | 子类使用 | 子类通过extends关键字可以继承一个抽象类 | 子类使用implements可以实现多个接口 |
5 | 两者关系 | 抽象类可以实现若干个接口 | 接口不可以继承抽象类,但是允许继承多个父接口 |
6 | 使用 | 1.抽象类或接口必须定义子类 2.子类一定要覆写抽象类或接口中的全部抽象方法 3.通过子类的向上转型实现抽象类或接口对象实例化 | 一样 |
当抽象类和接口都可以使用的情况下优先要考虑接口,因为接口可以避免子类的单继承局限,另外从正常的设计角度而言,也需要先从接口来进行项目的整体设计
案例分析一:
package com.company;
import java.util.Scanner;
/**
* @author wangdezhao
* 定义一个**ClassName**接口,接口中中只有一个抽象方法**getClassName**();
* 设计一个类**Company**, 该类可以实现接口**ClassName**中的方法**getClassName**(),
* 功能是获取该类的类名称: 编写应用程序使用**Company**类。
*/
public class JavaDemo{
public static void main(String[] args){
CompanyImpl c = new CompanyImpl();
System.out.println(c.getClassName());
}
}
interface IClassName{
public abstract String getClassName();
}
class CompanyImpl implements IClassName{
@Override
public String getClassName(){
return "Company";
}
}
案例分析二:
package com.company;
import java.util.Scanner;
/**
* @author wangdezhao
* 考虑一个表示绘图的标准,并且可以根据不同的图形来进行绘制
*/
public class JavaDemo{
public static void main(String[] args){
Factory f1 = new Factory();
Factory f2 = new Factory();
System.out.println(f1.getShape("circle").draw());
System.out.println(f2.getShape("Triangle").draw());
}
}
/**
* 实现不同图像的绘制
*/
interface IShape{
public String draw();
}
class TriangleImpl implements IShape{
@Override
public String draw(){
return "绘制一个三角形...";
}
}
class CircleImpl implements IShape{
@Override
public String draw(){
return "绘制一个圆形...";
}
}
class Factory{
public IShape getShape(String shape){
if("Triangle".equals(shape)){
return new TriangleImpl();
}else if("circle".equals(shape)){
return new CircleImpl();
}else{
return null;
}
}
}
案例分析三:
package com.company;
import java.util.Scanner;
/**
* @author wangdezhao
* 定义类Shape, 用来表示一般的二维图形, Shape具有抽象方法area和perimeter,
* 分别用来表示一般的二维形状类, 这些类均为Shape的子类。
* <p>
* 抽象类中使用工厂设计模式,叫做抽象工厂类
*/
public class JavaDemo {
public static void main(String[] args) {
Factory factory = new Factory();
System.out.println("圆形的面积为: " + factory.getShape("circle", 2.01).area());
System.out.println("圆形的周长为: " + factory.getShape("circle", 2.01).perimeter());
System.out.println("长方形的面积为: " + factory.getShape("rectangle", 3, 4).area());
System.out.println("长方形的周长为: " + factory.getShape("rectangle", 3, 4).perimeter());
}
}
abstract class AbstractShape {
public abstract double area();
public abstract double perimeter();
}
class Circle extends AbstractShape {
private double r;
public Circle(double r){
this.r = r;
}
public double getR() {
return r;
}
public void setR(double r) {
this.r = r;
}
@Override
public double perimeter() {
return 2 * 3.14 * this.r;
}
@Override
public double area() {
return 3.14 * this.r * this.r;
}
}
class Rectangle extends AbstractShape {
private double a;
private double b;
public Rectangle(double a,double b){
this.a = a;
this.b = b;
}
public double getA() {
return a;
}
public void setA(double a) {
this.a = a;
}
public double getB() {
return b;
}
public void setB(double b) {
this.b = b;
}
@Override
public double perimeter() {
return 2 * (this.a + this.b);
}
@Override
public double area() {
return this.a * this.b;
}
}
class Factory {
public AbstractShape getShape(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;
}
}
}
第十一章 包
11.1 包的定义与使用
操作系统中明确要求: 同一目录之中不允许存放有相同命名的文件,但是在开发过程中很难保证类的不重复, 所以为了可以对类进行方便的管理, 那么往往可以将程序文件放在不同的文件目录下,而这个目录就是包。包 == 目录
一旦定义了包, 此时程序编译后的结果就必须将*.class文件保存在目录之中, 但是如果手动建立非常的麻烦,那么此时应该进行打包操作
javac -d . Hello.java
- -d 表示要生成的目录
- " . " 表示在当前所在的目录生成类文件
在程序执行的时候一定要带着包执行程序类,也就是说以后完整的类名称就是: 包.类名称
// 定义包
package a.b;
public class Hello{
public static void main(String[] args){
System.out.print("Hello World !");
}
}
// 带包编译
// javac -d . a.b Hello.java
// 在当前目录下建立 a/b/Hello.class 程序类文件
11.2 包的导入
利用包的定义实际上就可以将不同的功能的类保存到不同的包之中,但是这些类彼此之间也会进行相互调用,这时就需要import语句来导入其他包中的程序类。
例子:
package wd.java.msg;
public class Message{
public void getContent(){
System.out.println("Message类~");
}
}
package wd.java.copy.msg;
import wd.java.msg.Message;
public class TextMessage{
public static void main(String [] args){
Message msg = new Message();
msg.getContent();
}
}
/*
打包操作:
javac -d *.java
java wd.java.copy.msg.TextMessage
*/
11.3 静态导入(很少用)
假如说现在有一个类, 这个类中的所有的方法都是static 方法,那么安装原始的做法肯定要导入程序所在的"包.类"。才可以通过类名称调用这些静态方法。
例子:
// MyMath.java
package wuhan.wd.java30th ;
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;
}
}
// TestMath.java
package wuhan.wd.java30th.test;
// import wuhan.wd.java30th.*;
import static wuhan.wd.java30th.MyMath.*;
public class TestMath{
public static void main(String[] args){
// MyMath m1 = new MyMath();
// System.out.println("5+6+7 = "+ m1.add(5,6,7));
System.out.println("5+6+7 = "+ add(5,6,7));
// 匿名对象
// System.out.println("5-7 = "+ MyMath.sub(5,7));
// 可以省去MyMath 相当于类内部函数直接调用
System.out.println("5-7 = "+ sub(5,7));
}
}
// 命令行命令
// javac -d . *.java
// java wuhan.wd.java30th.test.TestMath
/*
运行结果:
5+6+7 = 18
5-7 = -2
*/
第十二章 单例设计模式
12.1 单例设计模式
由于某些要求,现在要求一个Singleton类值允许提供有一个实例化对象,那么此时首先应该控制的就是构造方法。
- private 访问权限的主要特点为: 不能在类外部访问,但是可以在类本身调用,所有现在可以考虑在类的内部进行调用构造
class Singleton{
// 类内部进行调用构造
private Singleton instance = new Singleton();
// 构造方法私有化
private Singleton(){}
public String print(){
return "Singleton类正在被调用...";
}
}
public class JavaDemo{
public static void main(String [] args){
Singleton instance = null;
instance = Singleton.instance;
System.out.print(instance.print());
}
}
- 此时Singleton类内部的instance属于一个普通的属性, 而普通的属性实在对象实例化之后才能调用的, 而外部无法产生实例化对象,所以这个属性就不能被访问到了,所以这个属性就不能被访问到了, 那么就必须考虑如何在没有实例化对象的情况下访问属性, 则只有static 属性可以访问。
class Singleton{
// 使用static属性
static Singleton instance = new Singleton();
// 构造方法私有化
private Singleton(){}
public String print(){
return "Singleton类正在被调用...";
}
}
public class JavaDemo{
public static void main(String [] args){
Singleton instance = null;
instance = Singleton.instance;
System.out.print(instance.print());
}
}
- 类中的属性应该封装后使用,所以理论上此时的instance 需要被封装起来,那么就需要通过一个static方法获得。
class Singleton{
// 使用static属性
private static Singleton instance = new Singleton();
// 构造方法私有化
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
public String print(){
return "Singleton类正在被调用...";
}
}
public class JavaDemo{
public static void main(String [] args){
Singleton instance = null;
instance = Singleton.getInstance();
System.out.print(instance.print());
}
}
- 这个时候虽然提供有static的实例化对象,但是这个对象依然可以被重新实例化,所以需要保证Singleton内部的instance 无法再次实例化,所以应该使用final 来定义
class Singleton{
// 使用static属性
private static final Singleton INSTANCE = new Singleton();
// 构造方法私有化
private Singleton(){}
public static Singleton getInstance(){
return INSTANCE;
}
public String print(){
return "Singleton类正在被调用...";
}
}
public class JavaDemo{
public static void main(String [] args){
Singleton instance = null;
instance = Singleton.getInstance();
System.out.print(instance.print());
}
}
第十三章 异常处理
13.1 处理异常
package com.wangdezhao;
import com.sun.javadoc.ThrowsTag;
import com.sun.xml.internal.ws.developer.UsesJAXBContext;
import java.util.Scanner;
/**
* @author wangdezhao
* @date 2021/3/2 12:33
* Integer.parseInt(String) return integer
* Integer.valueOf(String) return int
*/
public class TryCatchDemo {
public static void div(double a, double b) throws ArithmeticException {
if (b == 0) {
throw new ArithmeticException("除数不能为0,我觉得你有问题~");
}else{
System.out.println("a / b = "+a/b);
}
}
public static void main(String[] args) {
try{
div(2,0);
}catch(ArithmeticException e){
System.out.println("发生了异常");
e.printStackTrace();
}
}
}
/*
运行结果:
发生了异常
java.lang.ArithmeticException: 除数不能为0,我觉得你有问题~
at com.wangdezhao.TryCatchDemo.div(TryCatchDemo.java:18)
at com.wangdezhao.TryCatchDemo.main(TryCatchDemo.java:26)
Process finished with exit code 0
*/
第十四章 泛型
14.1 泛型定义
如果想要避免CalssCastException ,最好的做法就是避免使用向下转型, 而泛型的本质就是:类中的属性或方法的参数和返回值类型可以由对象实例化时自动决定,需要在类定义的时候明确定义占位符(泛型标记)
package com.wangdezhao.day3;
/**
* @author wangdezhao
* @date 2021/3/3 08:37
* @description : 练习泛型
*/
//T是type的简写,可以定义多个泛型,此时x,y的数据类型是不确定的
class Point <T>{
private T x;
private T y;
public void setX(T x){
this.x = x;
}
public T getX(){
return this.x;
}
public void setY(T y){
this.y = y;
}
public T getY(){
return this.y;
}
}
public class Demo1{
public static void main(String[] args){
Point <Integer> p = new Point(){};
// 设置内容
p.setX(10);
// p.setY("20"); 这里因为需要一个Integer类型,而给了一个String就会报错
p.setY(20);
// 从里面获取数据
int x = p.getX();
int y = p.getY();
System.out.println("x = "+x);
System.out.println("y = "+y);
}
}
这样在编译时就会报错,不用等到执行的时候在报错了。可以解决大部分类对象的强制转换处理
注意:
- 泛型值允许使用引用类型,如果现在要操作基本数据类型,必须使用包装类。
- 从JDK 1.7 开始,泛型对象实例化可以简化为 Point point = new Point<>()
3月5日
ArrayList 遍历的三种方法
package com.wangdezhao.day4;
import java.util.*;
/**
* @author wangdezhao
* @date 2021/3/4
* @description 学习ArrayList
*/
public class Demo1 {
public static void main(String[] args){
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
System.out.println(list.toString());
// no.1
Iterator it = list.iterator();
while (it.hasNext()){
System.out.print(it.next()+"\t");
}
// no.2
System.out.println("");
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i).toString()+"\t");
}
// no.3
System.out.println("");
for (Integer a : list) {
System.out.println(a.toString());
}
}
}
linklist的一些用法
package com.wangdezhao.day5;
import java.util.Collection;
import java.util.LinkedList;
/**
* @author wangdezhao
* @date 2021/3/5
* @description : 练习linklist
*/
public class Demo4 {
public static void main(String[] args) {
Collection<String> s = new LinkedList<>();
s.add("d");
s.add("e");
s.add("f");
LinkedList<String> list = new LinkedList<String>();
list.add("a");
list.add("b");
list.add("c");
// 打印链表(不需要用fori,但是ArrayList直接打印是地址值)
System.out.println(list);
System.out.println("在链表头部添加元素 first");
list.addFirst("first");
System.out.println(list);
System.out.println("在链表结尾添加元素 last");
list.addLast("last");
System.out.println(list);
System.out.println("移除开头元素");
System.out.println(list);
// 输出的是删掉的元素
System.out.println(list.removeFirst());
System.out.println("移除结尾元素");
System.out.println(list);
// 输出的是删掉的元素
System.out.println(list.removeLast());
System.out.println("获取开头的元素");
System.out.println(list.getFirst());
System.out.println("获取结尾的元素");
System.out.println(list.getLast());
System.out.println("迭代方法");
// fori
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i)+" ");
}
System.out.println();
// foreach
for (String s1 : list) {
System.out.print(s1+" ");
}
System.out.println();
// public boolean addAll(int index, Collection c)
// 将一个集合的所有元素添加到链表的指定位置后面,返回是否成功,成功为 true,失败为 false。
System.out.println(list.addAll(1, s));
System.out.println(list);
// 清空链表
list.clear();
System.out.println(list);
// 清空后再插入s链表
list.addAll(s);
System.out.println(list);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B94pZnQO-1617178832135)(/Users/wangdezhao/Library/Application Support/typora-user-images/image-20210305194844733.png)]
Iterator 的用法
Java Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法,可用于迭代 ArrayList 和 HashSet 等集合。
Iterator 是 Java 迭代器最简单的实现,ListIterator 是 Collection API 中的接口, 它扩展了 Iterator 接口。
迭代器 it 的两个基本操作是 next 、hasNext 和 remove。
调用 it.next() 会返回迭代器的下一个元素,并且更新迭代器的状态。
调用 it.hasNext() 用于检测集合中是否还有元素。
调用 it.remove() 将迭代器返回的元素删除。
package com.wangdezhao.day5;
import java.util.*;
/**
* @author wangdezhao
* @date 2021/3/5
* @description 练习 iterator
*/
public class Demo5 {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<Integer>();
Collection<Integer> list1 = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
//集合想获取一个迭代器可以使用 iterator() 方法
Iterator<Integer>it = list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
一个小注意事项
package com.wangdezhao.day5;
import java.util.*;
/**
* @author wangdezhao
* @date 2021/3/5
* @description 练习 iterator
*/
public class Demo5 {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<Integer>();
Collection<Integer> list1 = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
//集合想获取一个迭代器可以使用 iterator() 方法
Iterator<Integer>it = list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
// 如果用it,则并不会进入循环
while(it.hasNext()) {
if(it.next() < 3) {
it.remove(); // 删除小于 3 的元素
}
System.out.println("第一个while循环");
}
// 这时需要新建一个 it2 迭代器,重新迭代,这样才会进入循环,事实证明,当迭代器迭代完毕之后,就不能再次迭代了,必须要重新初始化一个迭代器
Iterator<Integer>it2 = list.iterator();
while(it2.hasNext()) {
if(it2.next() < 3) {
it2.remove(); // 删除小于 3 的元素
}
System.out.println("第二个while循环");
}
System.out.println(list);
}
}
3月6日
Hashset 的一些用法
HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。
HashSet 允许有 null 值。
HashSet 是无序的,即不会记录插入的顺序。
HashSet 不是线程安全的, 如果多个线程尝试同时修改 HashSet,则最终结果是不确定的。 必须在多线程访问时显式同步对 HashSet 的并发访问。
HashSet 实现了 Set 接口。
package com.wangdezhao.day6;
import java.util.HashSet;
/**
* @author wangdezhao
* @date 2021/3/6
* @description HashSet用法
*/
public class Demo1 {
public static void main(String[] args) {
HashSet<String> s = new HashSet<>();
s.add("a");
s.add("a");
s.add("b");
s.add("b");
s.add("c");
System.out.println(s);
// 我们可以使用 contains() 方法来判断元素是否存在于集合当中:
System.out.println(s.contains("a"));
System.out.println(s);
// 如果要计算 HashSet 中的元素数量可以使用 size() 方法:
// 可以使用 for-each 来迭代 HashSet 中的元素。
for (String s1 : s) {
System.out.print(s1+" ");
}
System.out.println("");
// 删除集合中所有元素可以使用 clear 方法:
s.clear();
System.out.println(s);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q5xcQMFy-1617178832137)(/Users/wangdezhao/Library/Application Support/typora-user-images/image-20210306084635686.png)]
Hashmap的一些使用方法
package com.wangdezhao.day6.hashmap;
import java.util.HashMap;
/**
* @author wangdezhao
* @date 2021/3/6
* @description : HashMap的一些用法
*/
public class Demo1 {
public static void main(String[] args) {
HashMap<Integer, String> map = new HashMap<Integer, String>(2);
System.out.println("HashMap 类提供类很多有用的方法,添加键值对(key-value)可以使用 put() 方法:");
map.put(1,"a");
map.put(2,"b");
map.put(3,"c");
System.out.println(map);
System.out.println("结果: {1=a, 2=b, 3=c} 说明Hashmap不存在越界的问题,可以一直添加");
System.out.println("可以使用 remove(key) 方法来删除 key 对应的键值对(key-value):");
map.remove(1);
map.remove(2,"b");
System.out.println(map);
System.out.println("如果要计算 HashMap 中的元素数量可以使用 size() 方法:");
System.out.println(map.size());
System.out.println("替换 hashMap 中是指定的 key 对应的 value。");
System.out.println(map.replace(3, "c", "d"));
System.out.println(map);
System.out.println("getOrDefault() 方法获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值。");
System.out.println(map.getOrDefault(3, "没找到"));
System.out.println(map.getOrDefault(1, "没找到"));
System.out.println("删除所有键值对(key-value)可以使用 clear 方法:");
map.clear();
System.out.println(map);
}
}
object 类及其操作
Object clone() 方法用于创建并返回一个对象的拷贝。
clone 方法是浅拷贝,对象内属性引用的对象只会拷贝引用地址,而不会将引用的对象重新分配内存,相对应的深拷贝则会连引用的对象也重新创建。
由于 Object 本身没有实现 Cloneable 接口,所以不重写 clone 方法并且进行调用的话会发生 CloneNotSupportedException 异常。
泛型方法
一个泛型方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用。
下面是定义泛型方法的规则:
- 所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的)。
- 每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
- 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
- 泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)。
package com.wangdezhao.day6.generic;
import java.awt.print.Printable;
/**
* @author wangdezhao
* @date 2021/3/6
* @description
*/
public class Demo1 {
public static void main(String[] args) {
Integer[] arr1 = {1,2,3,4,5};
String[] arr2 = {"a","b","c"};
Double[] arr3 = {1.0,2.2,3.3,4.4,5.5};
System.out.println("arr1: ");
printArr(arr1);
System.out.println("arr2: ");
printArr(arr2);
System.out.println("arr3: ");
printArr(arr3);
}
// Invalid method declaration; return type required
// public <T> printArr(T[] arr){}
public static <T> void printArr(T[] arr){
for (T t : arr) {
System.out.print(t+" ");
}
System.out.println();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NyHVRyM0-1617178832138)(/Users/wangdezhao/Library/Application Support/typora-user-images/image-20210306225342468.png)]
下面的例子演示了"extends"如何使用在一般意义上的意思"extends"(类)或者"implements"(接口)。该例子中的泛型方法返回三个可比较对象的最大值。
public class MaximumTest
{
// 比较三个值并返回最大值
//<T extends Comparable<T>>
// 类型 T 必须实现 Comparable 接口,并且这个接口的类型是 T。只有这样,T 的实例之间才能相互比较大小。例如,在实际调用时若使用的具体类是 Dog,那么 Dog 必须 implements Comparable<Dog>
public static <T extends Comparable<T>> T maximum(T x, T y, T z)
{
T max = x; // 假设x是初始最大值
if ( y.compareTo( max ) > 0 ){
max = y; //y 更大
}
if ( z.compareTo( max ) > 0 ){
max = z; // 现在 z 更大
}
return max; // 返回最大对象
}
public static void main( String args[] )
{
System.out.printf( "%d, %d 和 %d 中最大的数为 %d\n\n",
3, 4, 5, maximum( 3, 4, 5 ) );
System.out.printf( "%.1f, %.1f 和 %.1f 中最大的数为 %.1f\n\n",
6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ) );
System.out.printf( "%s, %s 和 %s 中最大的数为 %s\n","pear",
"apple", "orange", maximum( "pear", "apple", "orange" ) );
}
}
package com.wangdezhao.day6.generic;
/**
* @author wangdezhao
* @date 2021/3/6
* @description
*/
public class MaximumTest
{
// 比较三个值并返回最大值
// <T extends Comparable<T>>
// 类型 T 必须实现 Comparable 接口,并且这个接口的类型是 T。只有这样,T 的实例之间才能相互比较大小。例如,在实际调用时若使用的具体类是 Dog,那么 Dog 必须 implements Comparable<Dog>
public static <T extends Comparable<T>> T maximum(T x, T y, T z)
{
T max = x;
// 假设x是初始最大值
if ( y.compareTo( max ) > 0 ){
max = y;
//y 更大
}
if ( z.compareTo( max ) > 0 ){
max = z;
// 现在 z 更大
}
return max;
// 返回最大对象
}
public static void main(String[] args)
{
System.out.printf( "%d, %d 和 %d 中最大的数为 %d\n\n",
3, 4, 5, maximum( 3, 4, 5 ) );
System.out.printf( "%.1f, %.1f 和 %.1f 中最大的数为 %.1f\n\n",
6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ) );
System.out.printf( "%s, %s 和 %s 中最大的数为 %s\n","pear",
"apple", "orange", maximum( "pear", "apple", "orange" ) );
}
}
- comparable源代码
public interface Comparable<T> {
/**
* Compares this object with the specified object for order. Returns a
* negative integer, zero, or a positive integer as this object is less
* than, equal to, or greater than the specified object.
*
* <p>The implementor must ensure <tt>sgn(x.compareTo(y)) ==
* -sgn(y.compareTo(x))</tt> for all <tt>x</tt> and <tt>y</tt>. (This
* implies that <tt>x.compareTo(y)</tt> must throw an exception iff
* <tt>y.compareTo(x)</tt> throws an exception.)
*
* <p>The implementor must also ensure that the relation is transitive:
* <tt>(x.compareTo(y)>0 && y.compareTo(z)>0)</tt> implies
* <tt>x.compareTo(z)>0</tt>.
*
* <p>Finally, the implementor must ensure that <tt>x.compareTo(y)==0</tt>
* implies that <tt>sgn(x.compareTo(z)) == sgn(y.compareTo(z))</tt>, for
* all <tt>z</tt>.
*
* <p>It is strongly recommended, but <i>not</i> strictly required that
* <tt>(x.compareTo(y)==0) == (x.equals(y))</tt>. Generally speaking, any
* class that implements the <tt>Comparable</tt> interface and violates
* this condition should clearly indicate this fact. The recommended
* language is "Note: this class has a natural ordering that is
* inconsistent with equals."
*
* <p>In the foregoing description, the notation
* <tt>sgn(</tt><i>expression</i><tt>)</tt> designates the mathematical
* <i>signum</i> function, which is defined to return one of <tt>-1</tt>,
* <tt>0</tt>, or <tt>1</tt> according to whether the value of
* <i>expression</i> is negative, zero or positive.
*
* @param o the object to be compared.
* @return a negative integer, zero, or a positive integer as this object
* is less than, equal to, or greater than the specified object.
*
* @throws NullPointerException if the specified object is null
* @throws ClassCastException if the specified object's type prevents it
* from being compared to this object.
*/
- 关于<T extends Comparable>中的extends问题
- 类只能实现接口
- 接口只能结成接口,而且可以多继承
泛型类
泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。
和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数,这些类被称为参数化的类或参数化的类型。
package com.wangdezhao.day6.generic;
/**
* @author wangdezhao
* @date 2021/3/7
* @description
*/
public class GenericClass {
public static <I> void main(String[] args) {
Student<String, Integer> student = new Student<>("张三",1001);
System.out.println(student.sId);
System.out.println(student.sName);
}
}
// 多个泛型参数用","隔开
class Student<N,I>{
N sName;
I sId;
public Student(){}
public Student(N sName, I sId) {
this.sName = sName;
this.sId = sId;
}
public String getInfo(){
return "name: "+this.sName+"\nID: "+this.sId;
}
public N getsName() {
return sName;
}
public void setsName(N sName) {
this.sName = sName;
}
public I getsId() {
return sId;
}
public void setsId(I sId) {
this.sId = sId;
}
}
泛型函数
静态泛型函数和常规泛型函数的定义方法,与以往方法的唯一不同点就是在返回值前加上来表示泛型变量
package com.wangdezhao.day7.generic02;
/**
* @author wangdezhao
* @date 2021/3/7
* @description
*/
public class GenericFunc {
public static void main(String[] args) {
f1("2");
f1(2);
Test t = new Test();
t.f2("zhangsan");
t.f2(20);
}
public static <T> void f1(T n){
System.out.println("这是一个静态泛型函数");
System.out.println(n.getClass());
System.out.println(n.toString());
}
}
class Test{
public <T> void f2(T n){
System.out.println("这是个普通泛型函数");
System.out.println(n.getClass());
System.out.println(n.toString());
}
}
运行结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rmpnTmVv-1617178832139)(/Users/wangdezhao/Library/Application Support/typora-user-images/image-20210307113355560.png)]
Comparator用法
class StudentComparator implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
// TODO Auto-generated method stub
if(o1.getScore()>o2.getScore())
return -1;
else if(o1.getScore()<o2.getScore())
return 1;
else{
if(o1.getAge()>o2.getAge())
return 1;
else if(o1.getAge()<o2.getAge())
return -1;
else
return 0;
}
}
}
public class ComparableDemo02 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Student stu[]={new Student("zhangsan",20,90.0f),
new Student("lisi",22,90.0f),
new Student("wangwu",20,99.0f),
new Student("sunliu",22,100.0f)};
java.util.Arrays.sort(stu,new StudentComparator());
for(Student s:stu)
{
System.out.println(s);
}
}
lambda表达式
- lambda 表达式的语法格式如下:
(parameters) -> expression 或 (parameters) ->{ statements; }
lambda表达式的重要特征:
- **可选类型声明:**不需要声明参数类型,编译器可以统一识别参数值。
- 可选的参数圆括号: 一个参数无需定义圆括号,但多个参数需要定义圆括号。
- **可选的大括号:**如果主体包含了一个语句,就不需要使用大括号。
- **可选的返回关键字:**如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
内部类
键盘类
package com.wangdezhao.day7.computer.keyboard;
import com.wangdezhao.day7.computer.IUsb;
/**
* @author wangdezhao
* @date 2021/3/7
* @description
*/
public class KeyboardImpl implements IUsb {
private String socketType;
private String keyboard;
public String getSocketType() {
return socketType;
}
public void setSocketType(String socketType) {
this.socketType = socketType;
}
public String getKeyboard() {
return keyboard;
}
public void setKeyboard(String keyboard) {
this.keyboard = keyboard;
}
public KeyboardImpl() {
}
public KeyboardImpl(String socketType, String keyboard) {
this.socketType = socketType;
this.keyboard = keyboard;
}
@Override
public boolean connect(){
System.out.println(this.keyboard+"正在尝试连接");
if("USb".equalsIgnoreCase(this.socketType)){
return true;
}else{
return false;
}
}
}
USB接口
package com.wangdezhao.day7.computer;
public interface IUsb {
/**
* 电脑和外设通过usb连接
*
* @return boolean
*/
public boolean connect();
}
Computer 外围类和Cpu内部类
package com.wangdezhao.day7.computer;
import com.wangdezhao.day7.computer.keyboard.KeyboardImpl;
/**
* @author wangdezhao
* @date 2021/3/7
* @description
*/
public class ComputerImpl implements IUsb {
// computer属性
public KeyboardImpl k;
private String graphicsCard;
// 内部类
public class Cpu{
// cpu属性
private String cooling;
// cpu行为
public void useGraphicsCard(){
System.out.println("调用"+ComputerImpl.this.graphicsCard+"显卡,并用"+this.cooling+"冷却");
}
// 构造方法
public Cpu() {
}
public Cpu(String cooling) {
this.cooling = cooling;
}
}
// computer行为
@Override
public boolean connect(){
if(k.connect()){
System.out.println("主机端反馈: "+this.k.getKeyboard()+"外设已经连接");
return true;
}
else{
System.out.println("主机端反馈: "+this.k.getKeyboard()+"外设连接失败,不能识别"+this.k.getSocketType()+"接口");
return false;
}
}
// 构造方法
public ComputerImpl() {
}
public ComputerImpl(KeyboardImpl k, String graphicsCard) {
this.k = k;
this.graphicsCard = graphicsCard;
}
}
运行结果:
电脑1配置:
双飞燕正在尝试连接
主机端反馈: 双飞燕外设已经连接
调用七彩虹3090显卡,并用九州风神冷却
电脑2配置:
黑寡妇正在尝试连接
主机端反馈: 黑寡妇外设连接失败,不能识别typeC接口
调用AMD 6800XT显卡,并用大霜塔冷却
Process finished with exit code 0
个人总结:
封装
- 封装就是简单java类
继承
- 继承会涉及到父类和子类, 通过关键字extends连接, Java不支持多继承,但是可以多重继承;
- 继承还可以通过implements关键字来实现,使用 implements 关键字可以变相的使java具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口(接口跟接口之间采用逗号分隔)。
- 继承还涉及到super和this关键字
- final 关键字声明的类没有子类,因此不可以被继承
多态: 多态就是同一个接口,使用不同的实例而执行不同操作
- 三个必要条件: 继承 、重写、父类引用指向子类(向上转型)
- 当子类对象调用重写的方法时,调用的是子类的方法,而不是父类中被重写的方法。要想调用父类中被重写的方法,则必须使用关键字 super。
抽象方法
- 如果一个类包含抽象方法,那么该类必须是抽象类。
- 任何子类必须重写父类的抽象方法,或者声明自身为抽象类。
- 抽象方法没有定义,方法名后面直接跟一个分号,而不是花括号。
static 关键字
- static修饰的方法一般称作静态方法,由于静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说,是没有this的,因为它不依附于任何对象,既然都没有对象,就谈不上this了。并且由于这个特性,在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都必须依赖具体的对象才能够被调用。而对于非静态成员方法,它访问静态成员方法/变量显然是毫无限制的。
- 如果说想在不创建对象的情况下调用某个方法,就可以将这个方法设置为static。我们最常见的static方法就是main方法,至于为什么main方法必须是static的,现在就很清楚了。因为程序在执行main方法的时候没有创建任何对象,因此只有通过类名来访问。
- static修饰的变量也称为静态变量,静态变量和非静态变量的区别是:静态变量被所有对象共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
- **static修饰代码块,**static关键字还有一个比较重要的作用就是用来形成静态代码块以优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来依次执行每个static块,并且只会执行一次。因此,很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。
- Java中的static关键字不会影响到变量的变量或者方法的作用域。在Java中能够影响到访问权限的只有private、public、protected这几个关键字。
- 在Java中是不允许使用static修饰局部变量的。这是Java语法的规定。
- 静态成员变量虽然独立于对象,但是不代表不可以通过对象去访问,所有的静态方法和静态变量都可以通过对象访问(只要权限足够)。
子类继承父类的加载顺序详解
一:单独类的加载顺序
静态变量、静态代码块(从上到下的顺序加载)
类的非静态变量,非静态块(从上到下的顺序加载)
构造函数
二:子类继承父类的加载顺序
父类静态变量、父类静态代码块(从上到下的顺序加载)
子类静态变量、子类静态代码块(从上到下的顺序加载)
父类的非静态变量、父类的非静态代码块(从上到下的顺序加载)
父类的构造方法
子类的非静态变量、子类的非静态代码块(从上到下的顺序加载)
子类的构造方法
package wangdezhao.day9.reflection;
/**
* @author wangdezhao
* @date 2021/3/25
* @description
*/
class Percent {
public Percent() {
System.out.println("percent 构造方法");
}
{
System.out.println("percent 构造代码块");
}
static {
System.out.println("percent 静态代码块");
}
}
class Child extends Percent {
public Child() {
// super();
System.out.println("child 构造方法");
}
{
System.out.println("child 构造代码块");
}
static {
System.out.println("child 静态代码块");
}
}
public class Demo{
public static void main(String[] args) {
/**
* percent 静态代码块
* child 静态代码块
* percent 构造代码块
* percent 构造方法
* child 构造代码块
* child 构造方法
*/
Child child = new Child();
}
}