JAVASE基础总结
Java运行环境
- 安装JDK文件,
- 进入计算机环境变量
- 添加新变量JAVA_HOME,变量值为JDK安装路径
- 在变量Path中添加%JAVA-HOME%bin/%JAVA-HOME%jre/bin
- Java -version版本
- javac
Java了解
- JavaEE(企业)
- JavaSE(标准)
- JavaME(微型)
- jdk(Java开发工具包)
[Java Development kit]
- jre(Java运行环境)
[Java runtime environment]
- jvm(Java虚拟机)
[Java virtual machine]
执行一个程序,先去当前文档找文件,再去环境变量找
Java语法规则
- 名称由:字母,数字,下划线,$组成;
- 不能以数字开头;
- 名字不能使用Java中的关键字;
- 坚决不能出现中文;
- 类名使用CamelCase风格,必须遵循驼峰式(首字母大写/首字母小写);
一、基础数据类型
四类八种
- 整型: byte short int long(长整形)
- 浮点型: float(单精度) double (双精度)
- 布尔值: boolean
- 字符串: char
整形
正数的原码,反码,补码都是本身。
负数的反码,除了符号位,其余取反。
补码=反码+1
byte | 1个字节(8) |
---|---|
short | 2个字节 |
int | 4个字节 |
long | 8个字节 |
浮点型
float: | 单精度(f);32位 |
---|---|
double | 双精度(d);64位 |
**1为正,0为负****
float`的内存表示指数位数有8位,范围是[0, 255],考虑偏移量实际的指数范围是[-127,128],不允许同时取0或者同时取1,也就是指数位的实际范围是[-126,127],而指数取-127和128时有其特殊含义。
字符型
char | 占2个字节 |
---|---|
字符串 | System.out.println(Integer.toBingary String(b)); |
布尔型
boolean | 一个字节 |
---|---|
boolean=flag | true/false |
1、取值范围:
- 整型 : byte -2^7 -> 2^7-1 10000000(-128) 其余的以此类推
- 浮点型 : 符号位 + 阶码(指数) + 尾数
- char 一个字符连个字节 (unicode)
2、字符发展
ASCII(七位) -> ISO8859-1(单字节) -> unicode(两个字节) -> utf-8(可变长的字符集,英文 1个字节,中文3个字节)
GBK GB2312 中国的国标字符集
3、变量
变量的类型 变量的名字 = 具体的值;
定义和赋值两部分
他在内存里开辟了一片空间,可以存数据
他可以反复的到处使用
变量其实就是一个引用,他指向堆内存的一块区域,我们想操作一个对象只需要使用它的引用即可
4、标识符命名规则(名字)
- 可以用_和$但是不建议使用
- 可以用数字和字母,但是数字不能开头
- 不能使用关键字 super this class int double
- 所有的名字必须驼峰式命名,变量、方法首字母小写,类名首字母大写。静态常量全部大写多个单词用下划线分割。
- 尽量少用拼音,如果要用就整体都是拼音,不能拼音加因为,比如(isKong)
5、强制转化
整型里边小转大自动转,大转小需要强转,应为可能会都是精度
整型和浮点型:浮点型转整型需要强转,反之不需要,但是他以科学计数法表示可能会丢失精度
char能不能和整型转: 就是两个字节,随便转,就能当成一个short。但是char类型的变量经过计算都是int。任何short,byte,int,char无论是逻辑运算还是数学运算结果都是int;
// 会保存,计算结果都是int
byte m = 1;
byte n = 3;
byte c = m + n;
二、运算符
1、逻辑运算符
与 :& 全部为真才是真,有一个是假就是假 串联电路 1 & 1 = 1; 0 & 0 = 0 ; 1& 0 = 0
或 :| 全部为假才是假,有一个为真就是真 并联电路 1 | 1 = 1 ; 1 | 0 = 1; 0 | 0 = 0
非 : ! 非真即使假,非假就是真
异或: ^ 相同为假 ,不同为真 1^1 = 0 ; 0^0 = 0; 1^0 = 1
双与( &&):短路运算符, 前边为假直接返回假,后边的就不进行计算了 (常用)
双或 ( ||):短路运算符, 前边为真直接返回真,后边的就不进行计算了(常用)
2、算术运算符
加 减 乘 除 取余(%)
- 写在前边是先运算结果赋值后,执行其它。
- 写在后边是先执行后赋值。
3、赋值运算符
- = : 把后边的值赋给前边的变量
- ++ : 自加一 arr[i++] 选运算,先把i给arr算,然后i=i+1 ; arr[++i] 选i=i+1,先把i给arr ; arr[i+1] 就是一个值i不会变
- -- : 自减一
- += : i += 6 如同与 i= i+6
- -= :i -= 6 如同与 i= i-6
4、位移运算符
- >> 有符号右移 ,右移一位大致相当于什么除以2,除非溢出的末尾是零
- << 有符号左移,右移一位大致相当于什么乘以2,溢出的首位是零
- >>> 无符号右移
class HelloWorld{
public static void main(String [] args){
int a = 8 << 63;
System.out.println(a);
int r = 8 ^ 5;
System.out.println(r);
short num3 = 130;
byte num4 = (byte)num3;
System.out.println(num4);
byte num5 = 102;
short num6 = num5;
System.out.println(num6);
}
}
5、三目运算符
- 条件 ? 结果一 : 结果二;
class HelloWorld{
public static void main(String [] args){
// condition ? result1 : result2
条件 结果1 结果2
int res = 4>5 || true ? 6 : 9;
System.out.println(res);
}
}
三、流程控制语句
1、if条件控制语句
if (i == 0){
} else {
}
if (i == 0){
}
if (i != 2){
}
2、switch(开关)流程控制语句
// i可以是 byte short int String enum(枚举) 的数据
switch (i){
case 1:
System.out.println();
break;
case 2:
System.out.println();
break;
default:
ystem.out.println();
break;
}
3、while循环语句
// 如果不是死循环,一定要相办法退出,退出机制
// 先判断满足条件才执行
boolean flag = true;
while (flag){
if (如果达到某种条件){
flag = false;
}
}
// 死循环使用场景也挺多
do {} while (条件) 无论如何都要执行一次
4、do语句
- 先执行,然后判断,看要不要继续执行
do{
System.out.println();
}while (i > 0);
System.out.println();
5、for语句
int i = 0;
for (; i < 10;){
i++;
}
// 1、定义了一个变量,这个变量可以定义在任何地方,只用作用域能访问到即可,
// 如果i定义在了方法里,他的作用域就是整个方法。如果定义在分号处,就只能在for循环内使用
// 2、进入的条件,这个条件能很复杂 (i !=4 || i != 7)只要结果是true或者false即可
// 3、一个条件,用于退出,不写就是没有条件,他就无法退出。i++ 能不能写在for循环内部,可以。
for (;;) 死循环
四、数组Array
1、定义
- 类型 名字[] = new 类型[长度];
- 类型 名字[] = {1,2,3}
- 类型 名字[] ;名字 = new 类型[]{1,2,3,4}
2、特性
- 一旦创建就不能改变,类型不能改,长度不能改
- length代表他的长度
- 下标从零开始,最后一个是length-1
- 名字[下标] = 0;
- 如果使用时超出下标,ArrayIndexOutOfBoundsException;
- 数组里边可以是基本类型,也可以是引用类型
3、几个例子
必须要会的
遍历、取最大最小值、求和、交换
int temp = arrp[i];
arr[i] = arr[j];
arr[j] = temp;
4、数组
int [] nums = new int [6];
nums [0] = 0;
nums [1] = 1;
nums [2] = 2;
System.out.println(nums[2]);
5、数组FindMax
int[] nums = {2,3,1,9,6,4};
int max = nums[0];
for(int i = 1;i < nums.length;i++){
if(nums[i] > max){
max = nums[i];
}
}
System.out.println(max);
6、数组FindMin
int[] nums = {2,3,1,9,6,4};
int min = nums[0];
for(int i = 0;i < nums.length;i++){
if(nums[i] < min){
min = nums[i];
}
}
System.out.println(min);
7、选择排序
int[] nums = {2,3,1,9,6,4};
for (int j = 0;j < nums.length-1;j++){
int min = j;
for (int i = j+1;i < nums.length;i++){
if (nums[i] < nums[min]){
min = i;
}
}
int temp = nums[j];
nums [j] = nums[min];
nums [min] = temp;
}
for (int i = 0;i < nums.length;i++){
System.out.println(nums[i]);
8、冒泡
int nums [] = {2,4,1,6,9,7};
for(int j = 0;j < nums.length-1;j++){
for(int i = 0;i nums[i+1]){
int temp = nums[i];
nums[i] = nums[i+1];
nums[i+1] = temp;
}
}
}
for(int i = 0;i < nums.length;i++){
System.out.println(nums[i]);
}
9、查找下标
int nums [] = {2,4,1,6,9,7};
int target = -1;
for(int i = 0;i < nums.length;i++){
if(nums[i] == 5){
target = 1;
}
}
System.out.println(target);
10、倒置(两极交换)
int temp = 0;
for (int i = 0; i < array.length/2 ; i++) {
temp = array[i];
array[i] = array[array.length-1-i];
array[array.length-1-i] = temp;
}
for (int i = 0; i < array.length ; i++) {
System.out.println(array[i]);
}
11、求和
public class XcVm {
public static void main(String[] args) {
int array[] = {1,3,4,13,12,71,17,177,32};
// 求和,个位不能为7 十位不能为7 必须是偶数
int sum = 0;
for (int i = 0; i < array.length; i++) {
if ( array[i] % 10 != 7 && array[i] / 10 % 10 != 7 && array[i] % 2 == 0){
sum += array[i];
}
}
System.out.println(sum);
}
}
五、封装方法
权限修饰符 (static静态)(
泛型) 返回值类型 名字(参数){}
1、权限修饰符
作用域 | 当前类 | 同package | 子孙类 | 其他package |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
friendly | √ | √ | × | |
private | √ | × | × |
2、返回值类型
void 代表如返回值,能不能用return,能用代表终止方法;
3、static 不是必须的
静态方法和属性是属于类对象的的 ,使用时不需要new对象,直接 类名.方法名字()
,类名.属性
实例方法(不带static) 先要new对象
4、名字,驼峰式首字母小写
5、参数
参数可以有多个,方法定义的地方叫形参
,方法调用的地方传入的参数叫实参
6、可变参数:
- 只能出现一次
- 必须放在最后
public void f1 (int... nums)
7、方法的重载
- 方法名一样
- 参数列表不一样
- 每一个方法都有个签名 就是用名字和参数签名的
六、面向对象
1、封装
需要什么对象,就创建一个什么类,万物皆对象(就是我没有)
public class User{
// 私有的属性
// 构造器 可以没有 ,可以有一个,也可有duoge
// 实例方法
// getter and Setter
// toString(如果需要)
}
可能还有工具类和常量类,工具类里一般大多都是静态方法,常量类里大多是静态的常量
public static final String ORDER_STATUS = "已发货";
1.1 属性
属性全部私有,使用getter取值,使用setter赋值、能做安全控制,比如我不想让别人设置值就不写setter,
1.2 方法
1.3 构造器
- new一个对象会主动调用他的构造器
- 构造器可有多个,可以有不同参数的构造器
- 构造器如果不写,或默认送你一个无参构造,一旦你写了有参构造方法,无参的就没有了,需要手写。
1.4值传递和引用传递的区别
使用基础数据类型时,会使用直传递。
引用传递,传进去的是内存地址 会改变指向对象的实际内容。
2、继承
- extends关键字,子类拥有父类(超类)一切(除了别private修饰的)。
- 构造子类是一定会先构造一个父类。
- 子类可以重写父类的方法。重写的方法需要加一个注解
@override
- Object是所有类的顶级父类
2.1重写(注解)
重写和重载的区别
重载:在一个类里面的两个方法,名字一样,参数列表不一个样。
重写:父亲里面的的方法,孩子继承了,但是不想要,然后重写了一个方法。 @Override 重写方法
2.2父子类构造问题
父类如果只有 有参构造器没有空参构造器 构造子类的时候必须
构造子类必须先构造一个父类,而且必须首先构造父类,必须是第一行。
/*构造子类必须先构造一个父类,而且必须首先构造父类,必须是第一行
*/
public Son(){
super("烫头");
System.out.println("123");
}
super 指向父类
this 指向自己
object 指向顶级父类 所有类都会继承一个默认的顶级父类object,不用写也可以默认继承。
2.3insance of和对象转型
instanof 意思是实例 ,前面传的是对象,后面是类(class)
左边的对象 是否属于右边 的类
父 转 子 强转
子 转 父 自动转
一个子类不能继承多个父类。
3、多态
- 有继承
- 有重写
- 有父类引用指向子类对象
List list = new ArrayList<>();
list.add(1)
3.1数据结构——链表
数组和链表的区别和优缺点总结
数组和链表是两种基本的数据结构,他们在内存存储上的表现不一样,所以也有各自的特点。
链表中各结点在内存中的存放位置是任意的。
链表与数组的主要区别
(1)数组的元素个数是固定的,而组成链表的结点个数可按需要增减;
(2)数组元素的存诸单元在数组定义时分配,链表结点的存储单元在程序执行时动态向系统申请:
(3)数组中的元素顺序关系由元素在数组中的位置(即下标)确定,链表中的结点顺序关系由结点所包含的指针来体现。
(4)对于不是固定长度的列表,用可能最大长度的数组来描述,会浪费许多内存空间。
(5)对于元素的插人、删除操作非常频繁的列表处理场合,用数组表示列表也是不适宜的。若用链表实现,会使程序结构清晰,处理的方法也较为简便。
例如在一个列表中间要插人一个新元素,如用数组表示列表,为完成插人工作,插人处之后的全部元素必须向后移动一个位置空出的位置用于存储新元素。
对于在一个列表中删除一个元素情况,为保持数组中元素相对位置连续递增,删除处之后的元素都得向前移一个位置。如用链表实现列表.链表结点的插人或删除操作不再需要移动结点,只需改变相关的结点中的后继结点指针的值即可,与结点的实际存储位置无关。
数组的特点
- 在内存中,数组是一块连续的区域。
- 数组需要预留空间,在使用前要先申请占内存的大小,可能会浪费内存空间。
- 插入数据和删除数据效率低,插入数据时,这个位置后面的数据在内存中都要向后移。
- 随机读取效率很高。因为数组是连续的,知道每一个数据的内存地址,可以直接找到给地址的数据。
- 并且不利于扩展,数组定义的空间不够时要重新定义数组。
链表的特点
- 在内存中可以存在任何地方,不要求连续。
- 每一个数据都保存了下一个数据的内存地址,通过这个地址找到下一个数据。 第一个人知道第二个人的座位号,第二个人知道第三个人的座位号……
- 增加数据和删除数据很容易。 再来个人可以随便坐,比如来了个人要做到第三个位置,那他只需要把自己的位置告诉第二个人,然后问第二个人拿到原来第三个人的位置就行了。其他人都不用动。
- 查找数据时效率低,因为不具有随机访问性,所以访问某个位置的数据都要从第一个数据开始访问,然后根据第一个数据保存的下一个数据的地址找到第二个数据,以此类推。 要找到第三个人,必须从第一个人开始问起。
- 不指定大小,扩展方便。链表大小不用定义,数据随意增删。
各自的优缺点
数组的优点
- 随机访问性强
- 查找速度快
数组的缺点
- 插入和删除效率低
- 可能浪费内存
- 内存空间要求高,必须有足够的连续内存空间。
- 数组大小固定,不能动态拓展
链表的优点
- 插入删除速度快
- 内存利用率高,不会浪费内存
- 大小没有固定,拓展很灵活。
链表的缺点
- 不能随机查找,必须从第一个开始遍历,查找效率低
3.2超级链表
package com.xinzhi;
/**
* @author
* @date 2020/11/2
*/
public class SuperLink {
private Node head;
/**
* 添加数据
* @param data
*/
public void add(Integer data){
// 1、让这个node变成头
Node newHead = new Node(data,null);
// 2、让新的头指向旧的头
newHead.setNext(head);
// 让新的头变成头
head = newHead;
}
public void delete(int index){
if (index == 0){
Node node = getNode(index);
head = node.getNext();
}else {
Node node = getNode(index-1);
node.setNext(node.getNext().getNext());
}
Node node = getNode(index-1);
}
public Integer get(int index){
return getNode(index).getData();
}
public void update(int index,Integer newData){
Node node = getNode(index);
node.setData(newData);
}
private Node getNode(int index){
Node node = head;
for (int i = 0; i < index; i++) {
node = node.getNext();
}
return node;
}
public void print(){
Node node = head;
while (node != null){
System.out.println(node.getData());
node=node.getNext();
}
}
}
package com.xinzhi;
/**
* @author
* @date 2020/11/2
*/
public class Node {
private int data;
private Node next;
public Node() {
}
public Node(int data, Node next) {
/**
* 具体存入的数据
*/
this.data = data;
/**
* 指向下一个节点的引用
*/
this.next = next;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
@Override
public String toString() {
return "Node{" +
"data=" + data +
", next=" + next +
'}';
}
}
package com.xinzhi;
/**
* @author
* @date 2020/11/2
*/
public class Test {
public static void main(String[] args) {
SuperLink superLink = new SuperLink();
superLink.add(2);
superLink.add(3);
superLink.print();
}
}
3.3泛型
泛型,即“参数化类型”。最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。
顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
特性:
- 泛型:泛型只在编译阶段有效。
泛型类
泛型类型用于类的定义中,被称为泛型类。
通过泛型可以完成对一组类的操作对外开放相同的接口。
-
最典型的就是各种容器类,如:List、Set、Map。
代表泛型
package com.xinzhi.Super_Array; // 定义的super接口,在超级链表中实现接口。 public interface Super
{ void add(T data); void delete(int index); T get(int index); void update(int index,T date); void print(); int size(); }
4、抽象类(abstract)和接口(interfa ce)
- 抽象类 : 拥有抽象方法的类,必须使用
abstract
修饰,可以有抽象方法,也可以没有。
继承抽象类必须实现它的抽象方法。
- 接口:全部都是抽象方法(没有方法体的方法),
interface
实现接口(implements
)必须实现所有的抽象方法
抽象类:既有抽象方法,也有具体的实现方法,也可以全部是抽象,也可以全部是实现。抽象方法需要关键字(abstract)来标明这个是抽象类。类是加在class前面,方法是加在返回值前面
接口方法:必须只有抽象方法,而且权限修饰符可以不用加,默认的是public,接口类需要关键字(interface)和(implements)来实现。接口方法:必须只有抽象方法,而且权限修饰符可以不用加,默认的是public,接口类需要关键字(interface)和(implements)来实现。
- 编程一定要面向接口编程。
5、Object的几个方法
- equals
- hashcode(hash算法)
- 重写equals必须重写hashcode,主要是因为有hashmap,hashset这类的集合
- toString()
6、this和super
this指向本实例
super指向父类的实例
this和super可以当构造器使用,但是如果想使用super()必须放在第一行;
7、类的加载顺序
- 父类的静态属性
- 父类的静态代码块
- 子类的静态属性
- 子类的静态代码块
- 父类的非静态属性
- 父类的非静态代码块
- 父类的构造器
- 子类的非静态属性
- 子类的非静态代码块
- 子类的构造器
(1).静态字段
public static String NAME = "父类的静态字段"
(2).静态代码块
// 类加载的时候就会调用
// 雷加载的时候 第一次主动使用就会加载这个类,就会把他加载到方法区内存里
static{
System.out.println("静态的属性"+NAME);
System.out.println("父类的静态代码块");
}
(3).子类静态字段
public static String Name = "子类的静态字段"
(4).子类静态代码块
static{
System.out.println("静态的属性"+NAME);
System.out.println("子类的静态代码块");
}
(5).父类的成员变量
private String hobby = "泡妞";
(6).父类的非静态代码块
// 非静态代码块
{
System.out.println("父类的非静态属性"+hobby);
System.out.println("父类的非静态代码块");
}
(7).父类的构造方法
public Father(){
System.out.println("父类构造器");
}
(8).子类的成员变量
private String hobby = "电子游戏";
(9).子类的非静态代码块
// 非静态代码块
{
System.out.println("子类的非静态属性"+hobby);
System.out.println("子类的非静态代码块");
}
(10).子类的构造方法
public Son(){
System.out.println("子类构造器");
}
8、类型强转
父类引用 -> 子类对象 不需要强转 向下转型
子类引用 -> 父类的对象 需要强转 向上转型
七、集合
集合是存东西的,能存很多同一种类型(包含他的子类)的对象。
1、Collection
2、list(列表)
- 需要定义泛型
- ArrayList LinkedList
- 区别:自己看
3、set
- set和list的区别
- list :一个是有序的,可重复 有序说的是插入的顺序和实际的位置相关
- set : 一个是无序的,不可重复
- hashset怎么判断一个对象是不是重复的,equals和hashcode这两个方法,如果两个对象不是用一个地址但是他们equal,并且hashcode一样就说明一样。
.ArrayList和LinkedList
// 本质是一个数组
List list = new ArrayList<>();
// 本质是一个链表
List list = new LinkedList<>();
和HashMap的运行原理一样
内存存了:Value = object key = value next;
HashSet存放数字的时候会用value值当作存放位置的
用增强for循环遍历 因为他没有下标
for(Integer i:set){
System.out.println(i);
}
利用Set将数组内的重复值全部删除
Set set = new HashSet<>(ist);
list = new SuperArray<>(set);
4、map(映射)
- 一听到hash就要想到无序
- hashmap最重要的集合,如果有兴趣可以看看我B站的hashmap源码解读,特别复杂。
- 存的是key,value键值对。
- hashmap的原理很重要,数据结构是
数组+链表+红黑树
4.1HashMap()
// 存入键值对
Map map = new HashMap<>();
map.put("banzhang",new User("ccc",null));
map.put("xuexi",new User("licuizhen",null));
System.out.println(map.get("xuexi"));
存的值 key value next三个东西
申请一个数组,默认长度为16个,用到一个哈希算法。
用增强for循环遍历 因为他没有下标
不可以排序
4.2TreeMap();
自动排序,按照数字大小或者字符首字母a-z排序
Map map = new TreeMap();
map.put("banzhang",new User("赵六",null));
map.put("a组长",new User("王五",null));
输出:User{username="王五",password='null'}
User{username="赵六",password='null'}
5、自动排序的map
TreeTap:数据结构是红黑树
,这种结构天然就是有序的,字符串会按照字典序排序。
TreeSet :底层就是一个TreeMap
6、集合的遍历方式
list的遍历方式: 普通for循环,增强for循环,迭代器(iterator)
set的遍历方式:不能用普通for循环,只能用增强for和迭代器
// 增强for循环
for (Integer integer : set) {
System.out.println(integer);
}
// 迭代器
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
hashmap的遍历方式: entrySet来遍历,还能用迭代器
// 第一种
for (Map.Entry entry : map.entrySet() ){
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
// 第二种
Set keys = map.keySet();
for (String key : keys) {
System.out.println(map.get(key));
}
// 第三种迭代器
Iterator> iterator = map.entrySet().iterator();
while (iterator.hasNext()){
Map.Entry next = iterator.next();
System.out.println(next.getKey());
System.out.println(next.getValue());
}
7、Iterator迭代器
循环遍历所有数组
HashSet set = new HashSet();
set.add(1);
set.add(2);
set.add(3);
set.add(4);
set.add(5);
Iterator iterator = set.iterator();
while (iterator.hasNext()){
Integer next = iterator.next();
iterator.remove();
System.out.println(next);
}
增强for循环不能调用Remove方法
Map遍历
Iterator>iterator = new map.entrySet().iterator();
while(iterator.hasNext()){
Map.Entry next = interator.next();
System.out.println(next);
}
八、泛型
Java泛型设计原则:只要在编译时期没有出现警告,那么运行时期就不会出现ClassCastException异常.
泛型:把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型
参数化类型:
- 把类型当作是参数一样传递
<数据类型>
只能是引用类型
1、泛型类
泛型类就是把泛型定义在类上,用户使用该类的时候,才把类型明确下来....这样的话,用户明确了什么类型,该类就代表着什么类型...用户在使用的时候就不用担心强转的问题,运行时转换异常的问题了。
- 在类上定义的泛型,在类的方法中也可以使用!
/*
1:把泛型定义在类上
2:类型变量定义在类上,方法中也可以使用
*/
public class ObjectTool {
private T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
用户想要使用哪种类型,就在创建的时候指定类型。使用的时候,该类就会自动转换成用户想要使用的类型了。
public static void main(String[] args) {
//创建对象并指定元素类型
ObjectTool tool = new ObjectTool<>();
tool.setObj(new String("钟福成"));
String s = tool.getObj();
System.out.println(s);
//创建对象并指定元素类型
ObjectTool objectTool = new ObjectTool<>();
/**
* 如果我在这个对象里传入的是String类型的,它在编译时期就通过不了了.
*/
objectTool.setObj(10);
int i = objectTool.getObj();
System.out.println(i);
}
2、泛型方法
前面已经介绍了泛型类了,在类上定义的泛型,在方法中也可以使用.....
现在呢,我们可能就仅仅在某一个方法上需要使用泛型....外界仅仅是关心该方法,不关心类其他的属性...这样的话,我们在整个类上定义泛型,未免就有些大题小作了。
- 定义泛型方法....泛型是先定义后使用的
//定义泛型方法..
public void show(T t) {
System.out.println(t);
}
- 测试代码:
用户传递进来的是什么类型,返回值就是什么类型了
public static void main(String[] args) {
//创建对象
ObjectTool tool = new ObjectTool();
//调用方法,传入的参数是什么类型,返回值就是什么类型
tool.show("hello");
tool.show(12);
tool.show(12.5);
}
3、泛型类派生出的子类
前面我们已经定义了泛型类,泛型类是拥有泛型这个特性的类,它本质上还是一个Java类,那么它就可以被继承
那它是怎么被继承的呢??这里分两种情况
- 子类明确泛型类的类型参数变量
- 子类不明确泛型类的类型参数变量
3.1 子类明确泛型类的类型参数变量
- 泛型接口
/*
把泛型定义在接口上
*/
public interface Inter {
public abstract void show(T t);
}
- 实现泛型接口的类.....
/**
* 子类明确泛型类的类型参数变量:
*/
public class InterImpl implements Inter {
@Override
public void show(String s) {
System.out.println(s);
}
}
3.2 子类不明确泛型类的类型参数变量
- 当子类不明确泛型类的类型参数变量时,外界使用子类的时候,也需要传递类型参数变量进来,在实现类上需要定义出类型参数变量
/**
* 子类不明确泛型类的类型参数变量:
* 实现类也要定义出类型的
*
*/
public class InterImpl implements Inter {
@Override
public void show(T t) {
System.out.println(t);
}
}
测试代码:
public static void main(String[] args) {
//测试第一种情况
//Inter i = new InterImpl();
//i.show("hello");
//第二种情况测试
Inter ii = new InterImpl<>();
ii.show("100");
}
值得注意的是:
- 实现类的要是重写父类的方法,返回值的类型是要和父类一样的!
- 类上声明的泛形只对非静态成员有效
4、 类型通配符
为什么需要类型通配符????我们来看一个需求.......
现在有个需求:方法接收一个集合参数,遍历集合并把集合元素打印出来,怎么办?
- 按照我们没有学习泛型之前,我们可能会这样做:
public void test(List list){
for(int i=0;i
上面的代码是正确的,只不过在编译的时候会出现警告,说没有确定集合元素的类型....这样是不优雅的...
- 那我们学习了泛型了,现在要怎么做呢??有的人可能会这样做:
public void test(List
这样做语法是没毛病的,但是这里十分值得注意的是:该test()方法只能遍历装载着Object的集合!!!
强调:泛型中的并不是像以前那样有继承关系的,也就是说
List
和List
是毫无关系的!!!!
那现在咋办???我们是不清楚List集合装载的元素是什么类型的,List
这样是行不通的........于是Java泛型提供了类型通配符 ?
所以代码应该改成这样:
public void test(List> list){
for(int i=0;i
?号通配符表示可以匹配任意类型,任意的Java类都可以匹配.....
现在非常值得注意的是,当我们使用?号通配符的时候:就只能调对象与类型无关的方法,不能调用对象与类型有关的方法。
记住,只能调用与对象无关的方法,不能调用对象与类型有关的方法。因为直到外界使用才知道具体的类型是什么。也就是说,在上面的List集合,我是不能使用add()方法的。因为add()方法是把对象丢进集合中,而现在我是不知道对象的类型是什么。
3.4.1设定通配符上限
首先,我们来看一下设定通配符上限用在哪里....
现在,我想接收一个List集合,它只能操作数字类型的元素【Float、Integer、Double、Byte等数字类型都行】,怎么做???
我们学习了通配符,但是如果直接使用通配符的话,该集合就不是只能操作数字了。因此我们需要用到设定通配符上限
List extends Number>
上面的代码表示的是:List集合装载的元素只能是Number的子类或自身
public static void main(String[] args) {
//List集合装载的是Integer,可以调用该方法
List integer = new ArrayList<>();
test(integer);
//List集合装载的是String,在编译时期就报错了
List strings = new ArrayList<>();
test(strings);
}
public static void test(List extends Number> list) {
}
3.4.2设定通配符下限
既然上面我们已经说了如何设定通配符的上限,那么设定通配符的下限也不是陌生的事了。直接来看语法吧
//传递进来的只能是Type或Type的父类
super Type>
设定通配符的下限这并不少见,在TreeSet集合中就有....我们来看一下
public TreeSet(Comparator super E> comparator) {
this(new TreeMap<>(comparator));
}
5、通配符和泛型方法
大多时候,我们都可以使用泛型方法来代替通配符的.....
//使用通配符
public static void test(List> list) {
}
//使用泛型方法
public void test2(List t) {
}
上面这两个方法都是可以的.....那么现在问题来了,我们使用通配符还是使用泛型方法呢??
原则:
- 如果参数之间的类型有依赖关系,或者返回值是与参数之间有依赖关系的。那么就使用泛型方法
- 如果没有依赖关系的,就使用通配符,通配符会灵活一些.
6、泛型擦除
泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛形的java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受到影响,这个过程称之为“擦除”。
九、多线程
- 工厂模式
- 单利模式
- 代理模式
进程和线程的区别: 一个软件运行就是一个进程,一个进程可以运行多个线程,我们何以使用多个线程同时执行提升效率。
多个线程同时执行的时候,互相之间没有必然的顺序,他是有cpu的调度决定的。
启动线程必须调用start方法,不能调用run,调用run只是方法调用,start是个native的本地方法,会调度cpu的资源,开辟线程。
继承
Thread
类,重写run
方法;
public class MyThread extends Thread {
public static void main(String[] args) {
MyThread t1 = new MyTread("t1");
t1.start();
}
public MyThread(){}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
while (true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我是:" + Thread.currentThread().getName());
}
}
}
实现
runnable
接口,实现run
方法;
public class MyRun implements Runnable {
public static void main(String[] args) {
Thread t1 = new Tread(new MyRun());
t1.start();
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
ThreadTest.SB.append(1);
}
}
}
1.单例模式(Singleton):
注意:
- 单例类只能有一个实例
- 单利必须自己创建自己的唯一实例
- 单例类必须含有所有其他对象提供这一实例
2.饿汉式单利
public class Calendar {
private final static Calendar CALENDAR = new Calendar ();
private Calendar(){};
public static Calendar getInstance(){
return CALENDAR;
}
}
3.懒汉式单利
public class Calendar2 {
//需要一个静态的成员变量保存咋们的实例;
private static Calendar2 CALENDAR2 ;
//私有化构造器,不让外边new;
private Calendar2(){};
//持有一个方法,这个方法能返回内存当中的实例;
public static Calendar2 getInstance(){
if (CALENDAR2 == null){
CALENDAR2 = new Calendar2 ();
}
return CALENDAR2;
}
}
4.字符串变成数组统计中元素的个数
package com.xinzhi;
import java.util.HashMap;
import java.util.Map;
/**
* @author:荆少奇
* @create:2020/11/7 16:04
*
*/
public class Test1 {
public static void main(String[] args) {
String content = "hello world hello world aa" + "bb cc db cc ww qq xx xxx";
//1.先全部搞成小写
content = content.toLowerCase ();
//2.字符串搞成数组
String[] worlds = content.split ("");
//3.创建一个hashmap保存结果
Map result = new HashMap<> (16);
//4.循环遍历数据一个单词一个单词看
for (String word:worlds){
//看一看hasmap里有没有这个key
//有的话value+1
if(result.containsKey (word)){
result.put (word,(Integer)result.get (word) + 1);
}else{
//没有的话放进去(word+1)
result.put (word,1);
}
//hashmap便利
for(Map.Entry entry:result.entrySet ()){
System.out.println (entry.getKey () + "出现" + entry.getValue () + "次了");
}
}
}
}
5.归并的思想和对比
package com.xinzhi;
/**
* @author:荆少奇
* @create:2020/11/7 15:15
*/
public class Test {
public static void main(String[] args) {
int[] arr1 = {1,3,5};
int[] arr2 = {2,4,6};
int[] concat = concat (arr1,arr2);
//把两个有序数组合并成一个数组,还有序
for (int i:concat){
System.out.println (i + " ");
}
}
private static int[] concat(int[] arr1,int[] arr2) {
//1.创建一个新数组
int[] temp = new int[arr1.length + arr2.length];
int left = 0;
int right = 0;
int newIndex = 0;
//退出循环的机制
while (arr1.length != left && arr2.length != right){
if (arr1[left] > arr2[right]){
temp[newIndex] = arr2[right];
right++;
}else {
temp[newIndex] = arr1[left];
left++;
}
newIndex++;
}
if(arr1.length == left){
for (int i = right; i < arr2.length;i++){
temp[newIndex++] = arr2[i];
}
}
if(arr2.length == right){
for (int i = left; i < arr1.length;i++){
temp[newIndex++] = arr1[i];
}
}
return temp;
}
}
二、多进程
电脑开了很多应用程序,一个程序一个进程
一个进程里边包含很多线程
Thread:
currentThread : 显示当前线程
Thread.sleep: 让线程睡几毫秒
-
start(): 开启一个新的线程
package currentThreadAndThis; public class MyThread extends Thread { public MyThread(){ System.out.println("当前线程的名字:"+Thread.currentThread().getName()); System.out.println("当前线程的名字:"+this.getName()); } @Override public void run(){ System.out.println("当前线程的名字:"+Thread.currentThread().getName()+" run=="+Thread.currentThread().isAlive()); System.out.println("当前线程的名字:"+this.getName()+" run=="+this.isAlive()); } } //启动类 package currentThreadAndThis; public class Run { public static void main(String[] args) { MyThread myThread=new MyThread(); //初始化Thread对象,方便调用start(); //此时myThread作为参数传入Thread中,其实是myThread委托thread去执行; Thread thread=new Thread(myThread); //初始化自定义线程名称 thread.setName("C"); //启动线程 thread.start(); } }
运行结果
- 当前线程的名字:main
- 当前线程的名称:Thread-0
- 当前线程的名字:C run == tru
- 当前线程的名字:Thread-0 run ==false**
并发
同时拥有两个或多个线程,如果程序在单核处理器上运行,多个线程将交替地换入或者换出内存,这些线程是同时「 存在 」的,每个线程都处于执行过程中的某个状态,如果运行在多核处理器上,此时,程序中每个线程都将分配到一个处理器核上,因此可以同时运行。并发就是多个线程操作相同的物理机中的资源,保证其线程安全,合理的利用资源。
三、线程安全问题初探(vector和hashTap)
1.异常
顶级父类:Throwable
Error 错误 :一旦发生错误程序就直接崩溃
Exception 异常:异常分为受查异常和运行时异常
受查异常:手动提前处理
运行时异常:手动做好检查
1. 返回异常发生时的详细信息
public string getMessage();
2. 返回异常发生时的简要描述
public string toString();
3. 返回异常对象的本地化信息。使用Throwable的子类覆盖这个方法,可以声称本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与getMessage()返回的结果相同
public string getLocalizedMessage();
4. 在控制台上打印Throwable对象封装的异常信息
public void printStackTrace();
四、Exception:异常
1.处理异常
1. 抛出异常(throws):当一个方法出现错误引发异常时,方法创建异常对象并交付运行时系统,异常对象中包含了异常类型和异常出现时的程序状态等异常信息。
2. 捕获异常:在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。
try{
}catch{
}
printStackTrace:能打出异常在哪
2.throws和throw有什么不同
位置不同:throws用在函数上,后面跟的是异常类,可以跟多个。
throw用在函数内,后面跟的是异常对象。
功能不同:throws用来声明异常,让调用者知道该功能有可能出现的问题,并由调用者给出预先的处理方式。
throw抛出具体问题的对象。语句执行到throw功能就结束了,跳转到调用者。并将具体的问题对象抛给调用者。
注意:throw语句独立存在,下面不要定义其他语句。因为执行不到throw下面的语句。
3.线程安全
线程安全是指多个线程在执行同一段代码的时候采用加锁机制,使每次的执行结果和单线程执行的结果都是一样的,不存在执行结果的二义性。
线程不安全就是不提供加锁机制保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据
4.解决多线程不安全的问题:
synchronizd:同步锁
1)同步代码块:
synchronized(同步锁)
{
//方法体
}
(2)同步方法:给多线程访问的成员方法加上synchronized修饰符
public synchronized void test(){
//方法体
}
5.String,StringBuilder,StringBuffer
运行速度:
StringBuilder > StringBuffer > String
在线程安全上:
StringBuilder是线程不安全的,而StringBuffer是线程安全的
- String:适用于少量的字符串操作的情况
- StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
- StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况
十、常用类
1、String
是引用数据类型,他是final的,一旦创建就不能改变。
String,StringBuffer,StringBuilder的区别 面试常问。
- String是不可变得,线程安全的
- SringBuffer是可变字符串,线程安全
- StringBuilder是可变字符串,线程不安全
- 当有大量字符串拼接的代码时,没有线程安全的要求就用StringBuilder(一般情况),有要求就用StringBuffer。
2、工具类
Date Math Collections Arrays Canlader
以前咱写的复制的算法可能工具类一个copy方法就搞定了,不需要自己写了。
Date date = new Date();
SimpleDateFormate s = new SimpleDateFormate("yyyy-MM-dd hh:mm:ss");
String DateString = s.parse(date);
十一、枚举
1、定义枚举类型
定义枚举类型需要使用enum关键字,例如:
枚举可以用equals来比较,也可以用 == 来比较
public enum Direction {
FRONT, BEHIND, LEFT, RIGHT;
}
Direction d = Direction.FRONT;
2、枚举与switch
枚举类型可以在switch中使用
Direction d = Direction.FRONT;
switch(d) {
case FRONT: System.out.println("前面");break;
case BEHIND:System.out.println("后面");break;
case LEFT: System.out.println("左面");break;
case RIGHT: System.out.println("右面");break;
default:System.out.println("错误的方向");
}
Direction d1 = d;
System.out.println(d1);
3、枚举类的方法
- int compareTo(E e):比较两个枚举常量谁大谁小,其实比较的就是枚举常量在枚举类中声明的顺序,例如FRONT的下标为0,BEHIND下标为1,那么FRONT小于BEHIND;
- boolean equals(Object o):比较两个枚举常量是否相等;
- int hashCode():返回枚举常量的hashCode;
- String name():返回枚举常量的名字;
- int ordinal():返回枚举常量在枚举类中声明的序号,第一个枚举常量序号为0;
- String toString():把枚举常量转换成字符串;
- static T valueOf(Class enumType, String name):把字符串转换成枚举常量。
4、枚举类的构造器
枚举类也可以有构造器,构造器默认都是private修饰,而且只能是private。因为枚举类的实例不能让外界来创建!
enum Direction {
FRONT, BEHIND, LEFT, RIGHT;//[在枚举常量后面必须添加分号,因为在枚举常量后面还有其他成员时,分号是必须的。枚举常量必须在枚举类中所有成员的上方声明。]
Direction()//[枚举类的构造器不可以添加访问修饰符,枚举类的构造器默认是private的。但你自己不能添加private来修饰构造器。] {
System.out.println("hello");
}
}
其实创建枚举项就等同于调用本类的无参构造器,所以FRONT、BEHIND、LEFT、RIGHT四个枚举项等同于调用了四次无参构造器,所以你会看到四个hello输出。
5、枚举类可以有成员
其实枚举类和正常的类一样,可以有实例变量,实例方法,静态方法等等,只不过它的实例个数是有限的,不能再创建实例而已。
enum Direction {
FRONT("front"), BEHIND("behind"), LEFT("left"), RIGHT("right");
private String name;
Direction(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Direction d = Direction.FRONT;
System.out.println(d.getName());
十二、流
java IO流的概念
java的io是实现和输入的基础,可以方便的实现数据的输入和输出操作。在java中把不同的舒润/输出源(键盘,文件,网络连接等)抽象表述为“流”(stream)。
关键字
input 输入 output 输出 stream 流 writer 字符输入流 reader 字符输入流 File 文件
只要会了字节流就都会了
字节流能处理一切
IO流的分类
一、按照不同的分类方式,可以把不同的类型。常用的分类三种:
1.按照流的流分向,可以分为输入流和输出流;
输入流:
- 输入流:只能从中读取数据,而不能向其写入数据;
- InputStream和Reader为基类
输出流
- 输出流:只能向其写入数据,而不能向其读取数据;
- OutputStream和Writer为基类
2.操作单元划分,可以划分为字节流和字符流;
字节流
- 字节输入流:InputStream基类
- 字节输出流:OutputStream基类
字符流
- 字符输入流:Reader基类
- 字节输出流:Writer基类
3.按照流的角色划分为节点流和处理流;
节点流
节点流:节点流从一个特定的数据源读写数据。即节点流是直接操作文件,网络等的流,例如FileInputStream和FileOutputStream,他们直接从文件中读取或往文件中写入字节流。
处理流
处理流:“连接”在已存在的流(节点流或处理流)之上通过对数据的处理为程序提供更为强大的读写功能。过滤流是使用一个已经存在的输入流或输出流连接创建的,过滤流就是对节点流进行一系列的包装。例如BufferedInputStream和BufferedOutputStream,使用已经存在的节点流来构造,提供带缓冲的读写,提高了读写的效率,以及DataInputStream和DataOutputStream,使用已经存在的节点流来构造,提供了读写Java中的基本数据类型的功能。他们都属于过滤流。
4、流的分类表
| 分类 | 字节输入流 |字节输出流|字符输入流|字符输出流|
分类 | 字节输入流 | 字节输出流 | 字符输入流 | 字符输出流 |
---|---|---|---|---|
抽象基类 | InputStream | OutputStream | Reader | Writer |
访问文件 | FileInputStream | FileOutputStream | FileReader | FileWriter |
访问数组 | ByteArrayInputStream | ByteArrayOutputStream | CharArrayReader | CharArrayWriter |
访问字符串 | StringReader | StringWriter | ||
缓冲流 | BufferedInputStream | BufferedOutputStream | BufferedReader | BufferedWriter |
转换流 | InputStreamReader | OutputStreamWriter | ||
对象流 | ObjectInputStream | ObjectOutputStream |
转换流能都将字符流转成字节流
5、InputStream
引入相关的类: InputStream ins =null;
构建输入流,例如FileInputStream:
ins =new FileInputStream(new File(path));
操控字节输入流的,所用的方法也是针对字节的。
常用方法:
二、小案例
1、文件的基本操作
@Test
public void testFile() throws Exception{
//创建文件
File file = new File("E:\\test\\b.txt");
file.createNewFile();
//查看文件夹下的文件
File file2 = new File("E:\\test\\b.txt");
String[] list = file2.list();
for (int i = 0; i < list.length; i++) {
System.out.println(list[i]);
}
//其他的方法自己查看
}
2、复制文件带有进度条
@Test
public void testFileInputStream() throws Exception {
File file = new File("E:\\test\\a\\233.mp4");
//拿到文件的大小
long dataLength = file.length();
//构建一个输入流,他的数据要流入内存,咱们的程序
InputStream inputStream = new FileInputStream(file);
//构建一个输出流,他的数据要从内存(咱们的程序)流到另一个文件夹
OutputStream outputStream = new FileOutputStream("E:\\test\\b\\233.mp4");
//新建一个水泵,能存一点水,每次对对1k
byte[] buffer = new byte[1024 *1024*50];
Long currentLength = 0L;
//如果read返回-1说明读完了
int len;
int showNumber = 0;
while ( (len = inputStream.read(buffer)) != -1 ){
outputStream.write(buffer,0,len);
currentLength += len;
//当下加载了百分之多少
int currentPer = (int)(((double)currentLength/dataLength)*100);
//目的是不重复显示
if(showNumber != currentPer){
showNumber = currentPer;
System.out.println("已经拷贝了百分之" + showNumber);
}
}
outputStream.flush();
outputStream.close();
inputStream.close();
}
3、字节流读文件
@Test
public void testInputStream() throws Exception{
//怼了一个输入流到文件上
InputStream wordInput = new FileInputStream("E:\\test\\a\\word.txt");
//建立缓冲区
byte[] bytes = new byte[1024];
int len;
while ( (len = wordInput.read(bytes)) != -1 ){
System.out.println(new String(bytes,0,len, Charset.forName("ISO8859-1")));
}
wordInput.close();
}
4、字符流对文件
@Test
public void testReader() throws Exception{
//怼了一个输入流到文件上
Reader reader = new FileReader("E:\\test\\a\\word.txt");
BufferedReader br = new BufferedReader(reader);
String str;
while ((str = br.readLine()) != null){
System.out.println(str);
}
reader.close();
br.close();
}
5、向文件里写内容
//这个用main方法测吧
public void testWriter() throws Exception{
//怼了一个输入流到文件上
Writer writer = new FileWriter("E:\\test\\a\\writer.txt");
BufferedWriter bw = new BufferedWriter(writer);
Scanner scanner = new Scanner(System.in);
while (true){
System.out.print("请输入:");
String words = scanner.next();
bw.write(words);
bw.flush();
}
}
6、StringReader就是往String上怼
@Test
public void testStringReader() throws Exception{
//怼了一个string
OutputStream os = new FileOutputStream("E:\\test\\a\\user.txt");
ObjectOutput oo = new ObjectOutputStream(os);
oo.writeObject(new User("王老师",3,4));
oo.flush();
oo.close();
os.close();
}
7、对象流就是对new出来的对象进行序列化
该对象必须实现Serializable接口,才能被序列化。
对象在内存就是一堆0和1,任何文件在内存都是0和1,都能转化成字节数组,进行保存或者网络传输。
对象序列化也是一样的,就是把内存的对象以字节数组的形式上保存,我们能保存在磁盘,或者在网络中传输。
import java.io.Serializable;
/**
* @author zn
* @date 2020/2/14
**/
public class User implements Serializable {
private String name;
private int age;
private int gander;
public User(String name, int age, int gander) {
this.name = name;
this.age = age;
this.gander = gander;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getGander() {
return gander;
}
public void setGander(int gander) {
this.gander = gander;
}
}
@Test
public void testObjectOut() throws Exception{
//怼了一个string
InputStream is = new FileInputStream("E:\\test\\a\\user.txt");
ObjectInputStream oi = new ObjectInputStream(is);
User user = (User)(oi.readObject());
System.out.println(user.getName());
is.close();
oi.close();
}
十三、异常
1、checked 检查性异常
去坐飞机,可能堵车,你只能提前走一会儿来预防,所以要在编译的时候就使用try,catch来预先捕获,并给出解决方案
FileNotFoundException IoException InterruptedException
这种异常继承自Exception, 必须捕获,并处理
2、运行时异常
去坐飞机,没带护照,这是你自己的原因,可以通过检查一下解决
ArrayIndexOutOfBoundsException ClassCastException 数学类异常(int i = 1 / 0)
这种异常继承RuntimeException,不需要捕获,需要通过检查来预防。
3、错误 error
stackOutOfMemoryError 比如递归出不去
4、自定义异常
很多时候我们需要自己定义一些异常来帮助我们处理一些业务。
十四、算法
1、排序
选择排序
冒泡排序
插入排序
希尔排序
快速排序
归并排序(分而治之,归并)
堆排序 完全二叉树 大根堆 小根堆
桶排序
基数排序
计数排序
2、查找
二分查找
package com.xinzhi;
/**
* @author 荆少奇
* @date 2020/11/6
*/
class BinarySearchNonRecursive {
public static void main(String[] args) {
//定义边界
int[] arr = {15,85,35,155,247,84,694};
int index = binarySearch(arr, 15);
if (index != -1) {
System.out.println("找到了,下标为:" + index);
}
}
//处理边界
private static int binarySearch(int[] arr, int target) {
int left = 0;
int right = arr.length - 1;
//查找中间值mid,还没找到
while (left <= right) {
int mid = (left + right) / 2;
if (arr[mid] == target) {
return mid;
} else if (arr[mid] > target) {
right = mid; // 向左找
} else {
left = mid; // 向右找
}
}
return -1;
}
}
2.时间复杂(O(n))
时间复杂度(Time complexity)是一个函数,它定性描述该算法的运行时间。
空间复杂度(Space Complexity)是对一个算法在运行过程中临时占用存储空间大小的量度,记做S(n)=O(f(n))。
随机数
Random random = new Random();
获取当前时间
long data = System.currentTimeMillis();
3.String
char charAt(值) 语法 :字符串名.charAt(值); 返回值为 char 类型。从字符串中取出指定位置的字符
sta.getBytes() 指定数值转成byte类型
public String concat(String str) 将参数中的字符串str连接到当前字符串的后面,效果等价于"+"。
String trim() 截去字符串两端的空格,但对于中间的空格不处理。
regionMatches(boolean b, int firstStart, String other, int otherStart, int length) 从当前字符串的firstStart位置开始比较,取长度为length的一个子字符串,other字符串从otherStart位置开始,指定另外一个长度为length的字符串,两字符串比较,当b为true时字符串不区分大小写。
4.可变参数
工具类
带有“s”的是工具类
工具类绝大多数为静态类
Arrays的折半查找法
int[] num = {15,85,35,155,247,84,694};
int i = Arrays.binarySearch(num,694);
System.out.println(i);
可变参数
int sum = sum(1,5,8,3,7,57);
System.out.println(sum);
public static int sum(int... sum){
int snum = 0;
for (int i = 0; i < sum.length; i++) {
snum += sum[i];
}
return snum;
可变参数必须放在最后,一个方法只有一个可变参数
单核排序和多核排序
单核
int[] num = {15,85,35,155,247,84,694};
Arrays.Sort(num);
for (int unm: num) {
System.out.println(unm);
}
多核
int[] num = {15,85,35,155,247,84,694};
//paraller是多核
Arrays.parallelSort(num);
for (int unm: num) {
System.out.println(unm);
}
扩容数组
int[] aa = Arrays.copyOf(num,100);
for (int unm:aa) {
System.out.println(unm);
}
Math数学类
向上取整
@Test
public void testMath(){
System.out.println(Math.ceil(2.5));
}
向下取整
@Test
public void testMath(){
System.out.println(Math.floor(2.5));
}
Data
public Date():分配Data对象并初始化此对象。以表示分配它的时间。
public Data(Long data):分配Data对象并初始化此对象,以表示自从基准时间,即1970年1月1日00:00:00 以来的指定毫秒数
3、链表
数组和链表的区别:
- 数组:按下标查找快,插入效率慢;
- 链表:查找慢,插入快。
4、队列和栈 FIFO FILO
5、hash算法,可以将任何的文件转化成一个定长的字符串。
hash碰撞: 当两个不同的文件生成的字符长一样了,就叫hash碰撞
回到hashmap 取余数后值一样就叫hash碰撞
6、递归
方法自己调用自己
一定要有出口,没有出口就会栈内存溢出。
典型的案例:斐波那契数列
/**
* 第一个 0 第二个是1 以后都是前两个的和
* 自己调用自己叫递归
* 完成斐波那契数列 自己找一个联系递归的例子
* @param count
* @return
*/
public static int fibonacci(int count){
if (count == 1){
return 0;
}
if (count == 2){
return 1;
}
if (count < 1){
System.out.println("您输入的不合法");
return -1;
}
return fibonacci(count - 1) + fibonacci(count - 2);
}
7、超级集合
package com.xinzhi;
/**
* @author
* @date 2020/10/31
*/
public class SuperArray {
private int[] array;
private int currentIndex = -1;
public SuperArray(int size) {
array = new int[size];
}
public SuperArray() {
this(10);
}
public void sort(){
for(int i = 0;i < currentIndex;i++){
for (int j = 0;j < currentIndex-j;j++){
if (array[j] > array[j+1]){
int temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
}
}
public SuperArray add(int data){
currentIndex++;
// 游标加完之后越界,就扩容
if (currentIndex >= array.length){
int[] temp = new int[array.length*2];
for (int i = 0; i < array.length; i++) {
temp[i] = array[i];
}
array = temp;
}
array[currentIndex] = data;
return this;
}
public void delete(int index){
if (index < 0 || index > currentIndex ){
System.out.println("您输入的下标不合法!");
return;
}
for (int i = index;i < currentIndex;i++){
array[i] = array[i+1];
}
currentIndex--;
}
public void print(){
System.out.println("--------结果----------");
for (int i = 0;i <= currentIndex;i++){
System.out.println(array[i]);
}
}
public String toStr(){
String res = "";
for (int i = 0;i <= currentIndex;i++){
if (i == currentIndex){
res += array[i];
}else {
res = res + array[i]+",";
}
}
return res;
}
public int[] get(){
return array;
}
}
package com.xinzhi;
/**
* @author
* @date 2020/10/31
*/
public class Test {
public static void main(String[] args) {
SuperArray superArray = new SuperArray();
superArray.add(3);
superArray.add(4);
superArray.add(1);
superArray.add(8);
superArray.add(6);
superArray.add(12);
superArray.sort();
superArray.delete(2);
superArray.print();
superArray.get();
}
}