1.psvm
public static void main(String[] args) {}
2.sout
System.out.println();
3.Shift+Ehter 换行
4.Shift+F6 重命名
5.Alt+Shift+Enter 数据类型转换
6.IDEA菜单栏Code→Generate→全选 生成构造器
⭐️经典算法思想1.冒泡排序
确定迭代次数是array.length - 1,确定每次迭代将最大值放在最后边
确定每次比较的次数array.length - 1 - 此次迭代次数
优化:
如果本身有序,可以设置flag,每次迭代但凡没有发生交换则都有序
如果某次迭代内后方全部有序,可减少区间长度
2021年11月11日 1.环境安装1>IntelliJ IDEA 2020.1.3
2>JDK——jdk-8u131-windows-x64
3>Git
2.基础知识1>计算机
hardware:CPU(只和内存联系)、内存、I/O
software:操作系统
2>IDEA
源码文件HelloWorld.java,经过编译(compile),生成类文件/字节码(byte code)文件HelloWorld.class,这种类文件可以在虚拟机上执行。class并不直接与机器的操作系统相对应,而是经过虚拟机间接与操作系统交互,由虚拟机将程序解释给本地系统执行,所以JVM是整个Java实现跨平台的最核心的部分。JDK中包含了JRE,JRE又包含JVM。
javac:java源文件、类文件(字节码文件)编译器(compiler)JVM(java virtual machine):java虚拟机,运行字节码的一个程序JDK(java development kit):java开发工具包,包含JRE、java工具(javac/java/jdb等) 、基础类库(Java API)JRE(java runtime environment):java运行时环境Intellij IDEA:一款IDE的产品 3.使用IDEA创建项目的过程分解
1> 新建工程
经由IDEA(程序)指定位置新建目录、新建配置文件
2> src新建一个HelloWorld 类
IDEA 指定位置(src目录)HelloWorld.java,文件中填充了一部分默认内容
3> IDEA 把代码写入 HelloWorld.java文件中
4> 点击run
编译 IDEA根据JDK所在目录,找到javac进行编译,HelloWorld.class运行 IDEA根据JRE所在目录,找到java运行类文件 4.类 class
类名==java文件名,可以使用Shift+F6 / rename改名称
命名要求:
不能以数字开头不能含有特殊符号(字母、数字以外的符号,下划线是允许的)不太建议使用中文类型
类名通常为HelloWorld,方法名、变量名通常为helloWorld,包名为hello_world
一个java源文件中只能有一个public类
类中包含方法,方法内包含语句
5.数据 data1>数据类型全部是内存上的数据
基本数据类型**(8类)**
数值类型**(7类)**
整型 byte short int long字符型 char,可以认为是一种有特殊地位的整型浮点型 float double
布尔类型**(1类)**
boolean,值为 true / false
引用数据类型**(3类)**
class类型 String s;interface类型 Flyable f;array类型 int[] t;
2>变量(本节重点)字节数:一字节=1byte=8位=8bit
数据类型 byte short int long char float double boolean 字节数(byte) 1 2 4 8 2 4 8 1 bit 不分有符号与无符号类型,最高位1代表负数,0代表正数。
使用Integer.MIN_VALUE可以获得int类型可以表示的最小十进制数:
int min = Integer.MIN_VALUE;
跳转2021年11月25日
变量==内存,变量属于内存上的一块区域
定义
变量类型 变量名称变量类型 变量名称 = 值
变量名称表示在内存的哪部分,变量数据类型决定变量内存的区域大小
种类
根据在代码中定义的位置,有不同的种类,决定了变量的不同:
作用域(scope)——代码视角
生命周期(life)——内存视角
全局变量
局部变量(local variable)
变量的作用域(scope)在{ }内,但对于{ }外定义的变量在{ }内进行的赋值是有效的;
局部变量语境下,如果一个变量只定义,没有赋值,无法直接使用(取其值)。
使用
赋值(出现在=的左边)取值(出现在=的右边)
3>基本数据类型的一定规则(类型转换和类型提升)
6.字面量 literalint a = 9;内的9就是字面量
字面量属于数据,默认类型是int。
| 字面量 | 字面量的类型 |
|---|---|
| 9 | int |
| 9L / 9l | long |
| 9.f / 9F | float |
| 9. | double |
| true / false | boolean(不允许boolean a =1) |
| ‘a’ | char |
| ‘xx’ | String |
整型字面量有不同的进制表示:二进制0b10,八进制0o10,十六进制0x10;或用科学计数法表示1.3e8,即1.3x10^8。
2021年11月14日 1.赋值操作 =【等号左边:以变量为代表的内存空间】a = 3【等号右边:值,可以是字面量、变量、表达式】
通过变量去保存一个字面量
2.类型转换(type cast)1>等号两边类型一致
2>类型不一致——类型转换
①类型提升 以参与运算的最高类型为主
②数值类型转换
int a=(int)10L
转换规则
char
double > float > long > int > short > byte
小赋值大 默认成立大赋值小 默认不成立数值类型和Boolean类型、引用类型无法转换
快捷键 Alt+Shift+Enter
字面量变量默认为int,但仍可以赋值
byte b = 1; short s = 2;这是java基于字面量变量的特殊规则,byte为-128~127,所以可以赋值[-128,127]的任何数
int c = 1/3; //int类型除以int类型,此处得0比如 1/3=0.333333,默认向下取整为0
3.字符串 Stringfinal可以修饰数据类型,只能被赋值一次
大小写转换 'y'+'A'-'a' = 'Y'
不是基本类型,是引用类型s+1表示的是字符串拼接操作与基本类型不能相互转换
int a = Integer.parseInt("1");
long l = Long.parseLong("1");
boolean b = Boolean.parseBoolean("true");
String str = String.valueOf(1.0f);
int aa = Integer.parseInt("11",2);//radix代表”11“的进制
System.out.println("二进制转换:"+aa);
4.运算符
运行错误
编译错误 不符合语法规则,IDEA会提示运行时错误 程序运行期间会抛出异常,但符合语法规则
a++
int a = 1; System.out.println(a++); //输出结果为1,先赋值再加1
逻辑运算符
运算都是Boolean类型遵循短路求值,比如或运算中,只要有一个算出来为false,就不进行后来的运算
位运算
&|~
移位操作
0b101 >> 1 0b10 可以视为a/2 5.逻辑控制语句
顺序结构
分支结构
if-elseif-else语句 无if(a)的条件swtich-case-break-default语句
循环结构
break 跳出循环
continue 跳过这次循环,开始下次循环
while语句
右键点击断点,设置条件,然后点击运行至下一断点处,可以跳过一部分循环 for语句do-while语句 作业
Java标识符由字母、数字、下划线“_”、美元符号“$”或者人民币符号“¥”组成。
首字母不能是数字,不能是关键字,且对大小写敏感。
闰年
2021年11月16日 1.输出闰年分为普通闰年和世纪闰年。
普通闰年:能被4整除但不能被100整除的年份为普通闰年。
世纪闰年:能被400整除的为世纪闰年。
System.out.println(); //方法调用system,只能有一个参数 System.out.print(); //输出不换行 System.out.printf(); //format格式化输出,可以有多个参数,且参数类型不确定。 //不换行,使用与C语言相同 //但可以使用%n替换n,r是回车2.输入
使用scanner扫描器
import java.util.Scanner;
Scanner scanner = new Scanner(System.in);
String line = scanner.nextLine();//单个输入
//一直等待输入,按Ctrl+D或Ctrl+Z结束
while(scanner.hasNextLine()){
String line = scanner.nextLine();
System.out.println(line);
}
| hasNext…… | next…… |
|---|---|
| hasNextLine | 行为单位 |
| hasNext | 以空格t分割的单词为单位 |
| hasNextInt | 以空格t分割的单词为单位,但转为整型 |
| hasNextLong | |
| hasNextDouble |
hasNext()为true时,nextInt()才可以取值 3.随机数
Java中的随机数都是伪随机数,Random random = new Random(2021);中2021为seed,此时固定时生成的随机数不变。
import java.util.Random; Random random = new Random(); int r = random.nextInt(100);//100表示边界,左闭右开
Random和Scanner都是引用类型。 4.方法 method 1>定义
要在类里定义,不能在其他方法里定义
public static 返回值类型 方法名称(形参列表){ 语句 return语句}
此处的参数列表是形参,形参其实是一种特殊的局部变量
无返回值可以将返回值类型设为void,return后的语句不能执行也不允许写
throw new RuntimeException("此处可以写提示");与return类似,此语句之后也不允许执行或写语句
方法没有调用,里面的语句永远不会被执行,main方法除外
同一个类下,暂时不允许同名方法
2>调用
方法的调用实质是值,能出现值的位置,即可进行方法的调用,并且方法可以嵌套调用
变量类型 变量名称 = 方法名称(实参)
隐含的赋值操作
实参赋值给形参方法调用的结果赋值给返回值返回值赋值给调用方法的变量
在同类调用时可以直接写方法名称,在其他类调用写类.方法名称
2021年11月18日 1.包 package用来放类的组织单位,包(文件夹)中含有很多的类(文件)通过package声明决定该类属于哪个包,类的实际全名称为 包名.类名使用某个包下的类方法
import 包名.类名;import 包名.*;包名.类名.方法 2.git的使用
历史博客《如何将IDEA文件提交至Gitee仓库》
3.方法的调用栈 call stack
先进后出
主要用于表现此刻方法的调用关系
栈帧(stack frame):每层元素
java中的栈(调用栈)、栈帧(frame)都是JVM管理的内存的某个名称
内存管理
内存(硬件)→OS→JVM→栈区(由栈帧组成:每个栈帧对应一次方法调用)
局部变量(包含形参)
在该变量对应的方法的某次栈帧上(方法的每次被调用,都有一个唯一的栈帧,方法执行结束,栈帧结束)
局部变量的生命周期
开始于方法开始执行:JVM 为本次方法允许分配好了栈帧
结束于方法执行结束:JVM 将方法执行中用到的栈帧回收
无论是基本数据类型还是引用类型,形参的改变不会影响实参(java中全部是传值调用)
2021年11月21日 1.方法重载 overloadC语言中一个编译单元(一个C文件)内不能出现同名的函数
Java中允许一个编译单元(一个Java类)中可以出现相同的函数(方法),但要求参数列表不同
方法的签名:Java编译单元中唯一确定方法的标识
签名 = 方法名 + 参数列表
2.静态属性只要求形参的类型不同,不要求形参的名称不同
签名不包括返回值类型,所以返回值类型相不相同没有关系
静态属性、类变量、静态变量是除了局部变量之外的另一种变量种类
定义位置:类里但在方法外
public static 数据类型 变量名 = 字面量变量;static{…} 按顺序执行作用域:在整个类的内部是有效的(大括号内)生命周期:伴随着类在运行中的生命周期而运行的初始化问题:伴随着类的生命周期,所以它在类加载的过程中就初始化完成,会在Main方法使用之前默认值是0的变形:0、false、null通过类名.变量名使用别的类下的静态属性变量 3.类的加载过程
将属于一个类的数据(以类文件为代表)从硬盘上加载到内存中
某个类被用到的时候才加载
类被用到(类名.*):
类内方法被用到的时
用到类的main方法时
用到类的静态属性时
加载的时候被加载的数据
静态属性方法→语句→字节码指令
被加载到方法区
4.访问权限(访问限定符)JVM中存在栈区和方法区
public:所有人都可以使用
protected:同一个包下的子类可以使用,子包下的子类中也可以使用,不同包下的子类中也可以使用,比包访问权限要大
不写,即default level(也称为包访问权限):同一个包的子类可以使用,否则就算是在子包下的子类中也不可以使用
private:只有自己类的内部可以使用
可以出现的位置:
类的内部、用来修饰 类变量、方法类 不可以用private 5.数组 Array 1>数组
一组具有相同类型的元素的集合
定义 int[] a
使用:只能用于初始化
int[] a = {1,2,3,4,5};
//定义一个变量a,其类型是数组
//该数组的元素类型是int
//并且进行了初始化操作
//该数组的大小已经确定为5
//该数组的元素分别为1、2、3、4、5
Random[] b={new Random(...),new Random(...)};
//这种使用是错误的
int[] a;
a[] = {1,2,3,4,5};
//可以使用以下语句
int[] a = new int[] {1,2,3,4,5};
//只规定长度,则默认为0的变形
int[] a = new int[3];
//等价于
int[] a = new int[] {0,0,0};
数组的长度一旦确定后就不可以更改了
//这是新创建了一个数组,用之前的数组a来使用 int[] a = new int[3]; a = new int[5];
数组的下标 index
类型:int范围:[0,数组的长度)
遍历数组:从前到后访问数组中的每个元素
需要知道数组的元素个数:a.length
//遍历方法
int[] a = {100,200,300};
System.out.println("方法一");
for (int i = 0; i < a.length ; i++) {
System.out.println(a[i]);
}
System.out.println("方法二");
for(int e : a){
System.out.println(e);
}
2>引用和对象
int[] a = {100,200,300};
a 引用 ⚪
{100,200,300} 对象 ⬜
每个对象都有自己的hash值(对象的指纹),对象不同,一般指纹不同
int[] a = b; //让b指向a现在指向的对象,只是一种引用,类似于共享
引用类型——形参的改变不会影响实参,但是
//5.使用交换数组方法但并没有进行交换
// 无论是基本类型引用还是引用类型,形参的改变不会影响实参(java中全部是传值调用)
// int[] a = { 1, 2, 3 };
// int[] b = {100, 200, 300 };
// swap(a, b);
// for (int i = 0; i < a.length ; i++) {
// System.out.println(a[i]);
// }
// for (int i = 0; i < b.length ; i++) {
// System.out.println(b[i]);
// }
//6.改变实参,因为此处改变的是对象的值,而不是引用
double[] arr = {1.0, 2.0, 3.0};
swap(arr, 0, 2);
System.out.println("double数组交换");
for (int i = 0; i < arr.length ; i++) {
System.out.println(arr[i]);
}
}
//5用到的swap方法,交换两个数组,但需要注意并不返回,因为java中全部是传值调用
// private static void swap(int[] a, int[] b) {
// int[] tmp = a;
// a = b;
// b = tmp;
// }
//方法重载,交换数组内类型为double
private static void swap(double[] array, int i, int j) {
double t = array[i];
array[i] = array[j];
array[j] = t;
}
3>使用中常见的两个异常
java.lang.ArrayIndexOutOfBoundsException
使用的下标不是合法下标时
java.lang.NullPointerException
对一个值是null的引用做解引用操作时
4>数组的操作函数int[] a = null; 让a引用不关联(指向)任何对象
解引用:通过引用要去动对象中的数据,比如a.length、a[0]
| 函数 | 作用 |
|---|---|
| sort(array) | 对整个数组排序 |
| sort(array, int fromIndex, int toIndex) | 对数组的[fromIndex,int toIndex)范围内进行排序 |
| binarySearch(array,key) | 使用二分查找算法搜索指定数组的key |
| equals(array1,array2) | 判断两个数组是否相等 |
| fill(array,element) | 使用element填充数组 |
| fill(array, int fromIndex, int toIndex,element) | 使用element填充数组的[fromIndex,int toIndex)范围 |
| copyOf(array, int newLength) | 复制数组到一个长度为newLength的数组中 |
| copyOfRange(array, int fromIndex, int toIndex) | 将指定数组的指定范围复制到新数组中,fromIndex不可以越界,toIndex可以越界,没有的用0补全 |
| toString(array) | 返回指定数组的内容的字符串表示形式 |
定义
long[][] a; 数组的类型是引用类型long[]
//long[] b; 数组的类型是long
long[][] a = new long[10][]; //左:创建一个数组a,数组的元素类型为long[] //右:新创建了一个元素类型为long[]、长度为10的对象,它是一个数组 //a是引用,右边的新建数组为对象2.面向对象 OOP
定义
对象:把一些相关属性一一绑定
三个层次
概念层 学生逻辑层 档案,包括学生姓名学号专业物理层 在内存中是怎么回事 3.类的第二大作用
类是构造对象的模板 1>属性
定义
//类的定义
class Person{
//属性的定义
String name;
int age;
}
//实例化对象的语法
Person p = new Person(要调用的构造方法的参数列表);
//通过引用使用引用指向对象的属性
p.name = ...;
... = p.name;
作用域:类内
属性的初始化规则(4种)
执行顺序为:默认值规则 → 定义时初始化规则和构造代码块规则按书写顺序 → 构造方法规则
默认值规则:不初始化则默认为0的变形
定义时初始化规则
class Person{
String name = 'aaa';
int age = 18;
}
构造代码块规则
{
//类内,方法外
//...
}
构造方法规则(构造器)
类似于方法,但是:
没有返回值类型的声明
构造方法的名称==类名
构造器重载
class Student{
//无其他构造方法会默认补此部分,内部为空
public Student(){
//...
}
//相当于方法重载
public Student(String a){
//...
}
}
2>this
作用
引用,类型就是当前类
指向当前对象:构造器种this指向的就是当前正在构造的对象
在一个构造方法中调用另一个方法
public Person(){
this(17);//调用有参构造器
}
public Person(int a){
//...
}
3>getter & setter
权限控制:权限最小原则
只读不改 getter
2021年11月25日行为:面向对象中的一个焦点,在Java中以实例方法的形式体现
实例方法:定义语法和之前一致,除了不要加static;
可以直接访问属性(和构造方法类似)
1.方法1>静态方法(类方法) 加static修饰——不可以访问属性,不可以使用this
2>普通方法(实例方法) 不加static修饰——可以访问属性,可以使用this
实例方法的调用,必须依赖一个对象(通过引用)的存在,如引用.普通方法()
| 普通方法 | 静态方法 | |
|---|---|---|
| 方法内部 | 有this 可以访问属性 可以调用普通方法 | 没有this 不能访问对象的属性 不能调用普通方法 |
| 方法外部 | 对象的引用去调用 “需要传入一个this" | 可以直接调用 “不需要传入this" |
在静态上下文(static context)中无法直接调用普通方法、访问对象属性。
static的理解:
**加了static,只和类有关,和对象无关。**反之,不加static,和本来的对象息息相关,不论属性和方法都是属于对象的。
变量
| 生命周期 | 区域 | |
|---|---|---|
| 局部变量 | 跟着方法运行走 | 栈帧>栈 |
| 类变量、静态变量、静态属性 | 跟着类的加载卸载走 | 类>方法区 |
| 实例变量、属性 | 对象实例化完成后出现,对象回收后消亡 | 对象>堆区 |
静态方法和普通方法都在方法区
⭐️OOP三大特性:封装(所以要设置访问权限)
概念层
现实生活中,万物皆对象(比如人物、进程等)
逻辑层
存在开发人员脑海中的世界,不止可以用Java实现
实际是对现实中一类有形或无形的概念的特征(特征表现为属性)抽取(对其行为的抽取表现为方法),比如人(人都有四肢、五官)、叶子
对象的正确性判定
对象职责不同,方法不同,比如一个人求职或者学习,他 学习的方法是不同的
变成代码的部分
物理层
对象在堆区
对象在堆区是如何存放的
对象是如何构建出来的
Person p = new Person();
3 1 2
//大体上有三个步骤
//1.
//通过类中的信息计算对象的内存大小(隐含着用到了类)
//在内存(堆区域)由JVM分配空间
//全部用0初始化
//半成品对象构建完成
//2执行初始化过程
//对象实例化→加载方法→调用初始化语句
//对象实例化完成
//3引用赋值
对象的死亡(不严谨):没有任何引用指向它,就可以视为死亡
引用赋值 2
浅拷贝赋值
藕断丝连似的赋值
对象内的引用指向的对象相同
深拷贝赋值
2.线性结构 List逻辑层:都属于线性结构
物理层:分为顺序结构和链式结构,也就是顺序表和链表
线性结构:
整体结构一般记为容器
装在容器中的单独的数据,称为元素、项、数据、值
除了第一个元素和最后一个元素之外,其他元素都有共性,即有前元素和后元素
第一个元素没有prev,有next,记为first、head最后一个元素没有next,有prev,记为last、tail
只有在线性结构中,下标才有意义,才讨论第几个元素,下标是表示第n个的代码表示,所以在线性结构中才有排序的含义
数组也算一种比较特殊的线性结构,在保存元素时,数组允许出现空洞,线性结构不允许出现
1>顺序表 ArrayList顺序表对象(在数组对象的基础上构建的),多加了自行控制容量的职责(表现为方法)
下标与元素个数size有关,与容量array.length无关
long[] newarray = Arrays.copyOf(array,newlength);//进行扩容
时间复杂度:
尾插尾删 O(1)根据下标插入删除时 O(n)get(index)/set(index,e) O(1)查找 O(n) 2>链表 linkedList ⭐️复杂度 complexity
一个评价程序(算法)好坏的标准从两个维度进行评定:
运行速度——时间复杂度占用空间——空间复杂度
1.时间复杂度 **O(1)< O(log(n)) < O(n) < O(n)*O(log(n)) < O(n^2)**前可行< O(n^3) < O(2^n)
直接掐表的缺陷:影响因素太多,无法控制变量
前提:给定CPU之后,单位时间内执行的指令条数基本恒定
所以,衡量算法的运行时间转换为衡量算法的执行指令个数
前提:Java中的基本指令(O(1)的语句)可以近似地看作指令的代替
即使固定算法,随着算法处理的数据规模的不同,算法需要的语句数也是不同的。比如冒泡排序改变数字个数。
所以:我们希望得到的时间复杂度是一个语句个数关于数据规模的函数关系 基本语句个数 = f(数据规模)
只保留函数关系中的最高次项把保留的项的常数系数变成1
步骤:找最坏的情况(去掉优化)、确定数据规模n、确定函数关系、大O表示法
时间戳:从1970-1-1 格灵威治时间到某个时间经过的秒数
long b = System.currentTimeMillis(); //以毫秒为单位的时间戳 //current 当前的Millis毫秒
规模n扩大多少倍,消耗的时间就扩大大O内的函数关系倍
假设在某台计算机上,冒泡排序50个数,耗费6秒的时间,冒泡排序500个数需要多长时间?
O(n个2) n的规模扩大了10倍,消耗的时间10^2 600s
单路递归一般是O(n)
二路递归(汉诺塔和斐波那契)一般是O(2^n)
数组的用下标访问时间复杂度为O(1)
2.空间复杂度
单路递归一般是O(1)
二路递归一般是O(n)
2021年11月30日 1.链表 linkedList结点 node
元素下一个结点的位置(引用)
通过头结点代表链表,即使没有头结点
等号左边一定是引用,等号右边一定是对象
作业:写一份顺序表代码
2021年12月2日 1.链表的特殊情况empty链表(空链表)只有一个结点的情况头结点、尾结点 2021年12月7日 1.链表操作
翻转、删除、合并、删除重复、分割复杂链表复制 2.链表对象
双向无头不循环
属性、方法、一致性分情况考虑:
空链表、有一个结点的链表、其他情况的链表头结点、尾结点、中间结点 重点:根据下标输入、删除 3.内部类
静态内部类——用来控制权限普通内部类 ⭐️static
1.静态方法
2.静态导包,省略Arrays
import static java.util.Arrays.*;2021年12月9日 1.继承
概念层的 is - a 关系
类A(动物)→ 类B(猫)
类B继承类A,类B派生于类A,类B扩展于类A
类A:父类(parent class)、基类(base class)、超类
类B:子类(sub class)、派生类(derived class)
class B extends A{
//B的方法
//IDEA提示的加粗的方法是自己的方法,其他的为继承的方法。
}
继承关系
不同类可以继承一个类,但不支持一个类继承多个类(多继承)class A{}隐含着类A继承自java.lang.Object类
逻辑上,子类对象中包含一个"父类对象",无论有没有权限访问。即类B有类A的全部方法和属性,只是一些可以访问,一些不可以。
子类对象实例化之前,必须在子类的构造方法中,调用父类的构造方法
通过super()调用
通过super可以明确访问父类,无歧义时通过this也可以访问父类super看到的是父类允许子类看到的(有访问权限的) 当父类有无参构造方法时,super()可以省略
子类加载前需要先把父类加载好,不管子类的构造器在哪里,都要先加载好父类
构造代码块在实例化的时候执行的
多态:对象有多种形态,比如动物有猫、狗等多种类型
方法重写时,即使是父类中的this.也是new的对象类型的this对象
⭐️能使用哪些方法,看引用类型;使用的是哪个对象的方法,看new的对象类型
向上转型是自然的,向下转型是有风险的,要进行风险测试if(Pet instanceOf Dog)才能转 2022年01月04日 1.复习
初始化顺序(父类在子类前)
类的加载化的初始化顺序对象实例化的加载顺序
多态的语法知识
2.abstract1>抽象类
抽象类被剥夺了实例化对象的能力(比如大的概念动物类),是用来继承的
可以引用Animal animal;,不能实例化对象new Animal();
抽象类与普通类的区别
抽象类不能实例化对象,允许出现抽象方法
“有抽象方法的是抽象类”是错误的
Java赋予了抽象类新的能力
允许定义一些方法,但不实现(没有方法体,只是声明,留给子类去必须重写,称为抽象方法)
2>抽象方法
抽象类与抽象方法的关系
抽象方法只能出现在抽象类中,不能出现在普通类中
“凡是抽象类中一定要出现抽象方法”是错误的
例子
比如线性表(抽象)类定义’头插’方法(其中包含’按照下标插入’),链表和顺序表中’定义按照下标插入’方法
3.final1>最终变量
只能赋值一次
2>最终类
通过final修饰的类,无法被继承
抽象类、普通类、最终类都允许出现一种方法——最终方法
3>最终方法
通过final修饰的方法,不允许被重写允许出现在任何类中,出现在最终类中意义不大(因为最终类无法被继承,其中的方法自然不会被重写) 4.类的三种职责
①放置静态属性/方法的位置②实例化对象③被继承
| 类的类型 | 备注 |
|---|---|
| 抽象类 | ③ |
| 普通类 | ②③ |
| 最终类 | ② |
可以理解为一种极端的抽象类(全部只有抽象方法的抽象类),所以不可以被实例化对象,它描述的是一个概念
不会出现属性(其实可以出现,但是是特定的)
只能出现方法(默认是抽象方法,并且被public修饰)
与抽象类的差异
类描述现实中的一类事物:抽象事物(比如动物,线性表)和具体事物(狗、顺序表);
接口大多时候只是一组能力的聚合,描述具备某种能力的东西(比如会飞,会跑),具体是什么事物不知道
类可以被继承,只允许单继承,extends
接口是用来被实现的,允许一个类同时实现多个接口implements,并且接口本身也允许多继承自其他接口
6.Cloneable 接口具备克隆能力,拷贝的对象是浅拷贝的
当某个对象被Cloneable修饰时,JVM内部会对其特殊处理,允许
SomeClass sc = new SomeClass(); SomeClass copy = clone sc; //对象复制(拷贝/克隆)过程7.对象的比较(一)
同一性 同一个东西相等性 同类东西,但不是同一个相似性 同内容东西,比如是版本2
Java中没有原生提供相似性判断逻辑的逻辑,但是有相等性判断的约束
重写的"equals"是相等性比较(默认是同一性比较):在Object中有一个equals(相等)方法,所有类继承自Object类,所以都有equals方法
"=="是同一性比较:对于基本类型来说同一性就是相等性,对于引用类型来说同一性只是同一性
2022年01月06日 1.对象的比较(二) 1>comparable 接口p1.compareTo(p2); //重写compareTo方法 // this > obj 返回 >0 // this < obj 返回 <0 // this == obj 返回 ==2>comparator 接口
comparator(p1,p2); //重写compare方法2.Java中的字符串
String常见方法:
从前往后 indexOf() 4种:目标为String或char两种+给定/不给定form两种
从后往前 lastIndexOf() 4种
2022年01月09日 1.方法 String s;
//判断是否以括号内字符串为前缀
boolean b = s.startsWith("字符串");
boolean b = endsWith("字符串");
//判断是否包含括号内字符串
boolean b = s.contains("字符串");
//全部替换,使用一个字符(串)去替换另一个字符(串)
s.replace('被替换字符', '替换字符');
s.replace("被替换字符串", "替换字符串");
//全部替换
s.replaceAll("被替换字符串", "替换字符串")
//只替换第一个
s.replaceFirst("被替换字符串", "替换字符串")
//截取字符串 左闭右开
substring(首下标,[尾下标]) //不输入尾下标,默认为长度
//以括号内字符分割字符串为数组类型
String[] parts = s.split("\.");
//数组通过连接符连接
String v = String.join("delimiter连接符",arr)
//修剪字符串两边的空白字符(包含空格、制表符(Nt)、回车、换行)
s.trim()
2.字串的不可变特性及其优化
1>不可变特性
对象一旦创建之后,无法被任何方式修改
成本:每次修改字符串,需要创建对象
收益:简化开发难度,不会让别人更改你给的东西
如何做到:
不可被继承 类被final修饰
属性被final修饰
防止内部的属性对象逃逸
只能保证"指向"不指向其他对象,但不能保证指向的对象内内容不能更改,会造成属性逃逸
2>优化
JVM使用池化(pooled)技术进行优化
逻辑上创建一个字符串对象池,这个对象池常见的术语有:哈希池
入池对象:
写在代码中的字面量字符串默认入池
new出来的对象默认不会入池
通过s.intern();强制s对象入池
如果s指向的对象已经在池里,返回池里的对象
如果不在,则将对象放入池中
3>结论
所以只比较对象的相等性,不去关心同一性的问题
3.StringBuilder 构造器从结构上完全可以把它当成一个关于字符的顺序表。
append(); toString(); //…… reverse(); replace(); substring();
StringBuffer是StringBuilder的早期版本,性能极低但是线程安全的(没有价值)。
4.异常针对代码中的非正常情况进行处理的
1>异常
优点:避免层层上报直到某个层级才可以处理该错误,发生异常就跳到能处理异常的地方
缺点:采用异常机制会带来性能损失
异常情况和正常情况完全分离
异常走throw形式正常走return形式
2>关键字
try + catch + finally
可以写try + catch,try + finally,try + catch + finally。
谁对异常负责,谁写try catch。
执行顺序:finally一定执行
try{
//正常执行的语句
}catch(异常的类型1 变量名){
//}catch(RuntimeException exc){
//遇到 异常类型的异常,该进行的操作
}catch(异常的类型2 变量名){
//……
}finally{
//必须执行的语句
}
throw 抛出异常
throw new 异常类型的对象(引用)
throws 声明风险
public void 方法(...) throws 异常1,异常2{
//......要使用方法就要承担风险异常
}
这样就可以使调用者用catch处理可能会出现的风险,不会让它没人负责
3>异常对象的构造方法
①无参构造方法
②允许传入String Message
③通过一个异常构造另一个异常
饭不够吃异常 e = ...; 物资不够异常 exc = new 物资不够异常(e); 由于物资不够异常,所以导致饭不够吃异常
4>Java程序中输出内容的通道
标准输出:一般用来输出非错误信息
标准错误:一般用来输出错误信息
目的地默认情况的情况都是控制台,只是走两条路,异常一般走的是紧急通道。都走正常通道先出发的一定先到达,但走两条路不一定谁先到达。
5>异常负责
由下向上,如果没人负责,由JVM负责,终止程序
6>编码规范
不要吞异常(即catch异常啥也不干),可以把原有的异常作为原因写在catch里
public void method3() throws IOException{
//抛受查异常的时候必须要用throws声明
throw new IOException();
}
//问题:使用method3方法会有IOException异常
//解决方法一 声明
//public void method4() throws IOException{
//解决方法二 catch
//实际中用非受查异常RuntimeException将受查异IOException常包起来
public void method4(){
try{
method3();
}catch(IOException exc){ //解决传染性问题
throw new RuntimeException(exc); //会输出exc信息,看到原因
}
}
7>异常分类
错误
继承自Error类的就是错误,也可以算作一种非受查异常因为设备等其他硬性环境导致的,程序无法修复的问题,比如CPU烧了
受查异常(检查异常)
继承自Exception类,但不属于RuntimeException子类的
程序代码BUG,比如空指针异常,数组下标越界异常
使得代码具备传染性
使用方法就要么用catch处理要么也声明异常列表,可以使用非受查异常把受查异常包起来,哪个类关心这个异常的时候哪个类处理它
非受查异常
继承自RuntimeException类的经过重试、程序自动修复解决的问题,比如内存不够用,网络不稳定
我们应该catch受查异常,不catch非受查异常,错误catch了也解决不了。由于实际的复杂性,这种规则一般不太遵守。
Java对于受查异常,强制要求:
一个方法如果抛出了受查异常,则必须通过throws声明一个方法如果抛出了非受查异常,可以声明也可以不声明
8>自定义异常 一般继承自RuntimeException
2022年01月16日throws 异常1,异常2{
//…要使用方法就要承担风险异常
}
这样就可以使调用者用catch处理可能会出现的风险,不会让它没人负责 **3>异常对象的构造方法** ①无参构造方法 ②允许传入String Message ③通过一个异常构造另一个异常 ```java 饭不够吃异常 e = ...; 物资不够异常 exc = new 物资不够异常(e); 由于物资不够异常,所以导致饭不够吃异常
4>Java程序中输出内容的通道
标准输出:一般用来输出非错误信息
标准错误:一般用来输出错误信息
目的地默认情况的情况都是控制台,只是走两条路,异常一般走的是紧急通道。都走正常通道先出发的一定先到达,但走两条路不一定谁先到达。
5>异常负责
由下向上,如果没人负责,由JVM负责,终止程序
6>编码规范
不要吞异常(即catch异常啥也不干),可以把原有的异常作为原因写在catch里
public void method3() throws IOException{
//抛受查异常的时候必须要用throws声明
throw new IOException();
}
//问题:使用method3方法会有IOException异常
//解决方法一 声明
//public void method4() throws IOException{
//解决方法二 catch
//实际中用非受查异常RuntimeException将受查异IOException常包起来
public void method4(){
try{
method3();
}catch(IOException exc){ //解决传染性问题
throw new RuntimeException(exc); //会输出exc信息,看到原因
}
}
7>异常分类
错误
继承自Error类的就是错误,也可以算作一种非受查异常因为设备等其他硬性环境导致的,程序无法修复的问题,比如CPU烧了
受查异常(检查异常)
继承自Exception类,但不属于RuntimeException子类的
程序代码BUG,比如空指针异常,数组下标越界异常
使得代码具备传染性
使用方法就要么用catch处理要么也声明异常列表,可以使用非受查异常把受查异常包起来,哪个类关心这个异常的时候哪个类处理它
非受查异常
继承自RuntimeException类的经过重试、程序自动修复解决的问题,比如内存不够用,网络不稳定
我们应该catch受查异常,不catch非受查异常,错误catch了也解决不了。由于实际的复杂性,这种规则一般不太遵守。
Java对于受查异常,强制要求:
一个方法如果抛出了受查异常,则必须通过throws声明一个方法如果抛出了非受查异常,可以声明也可以不声明
8>自定义异常 一般继承自RuntimeException



