admin管理员组

文章数量:1588157

java知识

  • 一、java特点
    • 1.java特点
    • 2. java运行机制
    • 3.书写规范、标识符
    • 4.注释
    • 5.转义字符
    • 6.扫描器
    • 7.引用变量
      • String,StringBuffer和StringBuilder区别
  • 二、if、while、for
    • 1、if 判断
    • 2、while 循环
      • while与do-while区别
    • 3、for 循环
    • 4、其他
      • a++与++a区别
      • 局部变量、实例变量的区别
      • for,break,continue区别
      • 函数(方法)
      • 访问修饰符区别
      • 重载、覆盖的区别
      • 递归
  • 三、数组
    • 1.数组的定义
      • 堆、栈区别
    • 2.数组的扩容
      • 可变长参数
    • 3.数组的插入
    • 4.选择、冒泡(排序)
      • 选择排序法
      • 冒泡排序法
      • 系统排序法
  • 四、面向对象
    • 1.java的发展过程
    • 2.面向对象
      • 类、方法的结构
      • 重载、覆盖
      • 局部变量、实例变量的区别
      • 构造方法
      • 类加载
        • 类加载的时机
      • 对象的创建过程(实例变量三次赋值机会)
    • 3、this, supper使用
      • this调用属性、方法名
      • this(),this(xx)区别
      • supper的使用
      • this与supper的区别
  • 五、面向对象三大特性
    • 1.封装
    • 2. 继承
      • 重载与覆盖
      • 继承特点
      • this、supper区别
    • 3.多态
      • 多态三句话
      • 强转转换
      • 多态使用的位置
      • 多态作用
  • 六、三大修饰符 abstract、static、final
    • 1.abstract 抽象
      • 语法
        • 抽象方法:
          • 抽象类
      • 抽象类常见疑惑
    • 2. static 静态
      • 静态属性
      • 静态方法
      • 特点
      • 动、静态代码块--类加载
        • 动态代码块(了解)
        • 静态代码块
      • 类加载
        • 类加载的使用
    • 3.final
      • final的属性-->常量
      • final的方法
      • final的类
      • private、final、static与abstract的关系
  • 七、接口
    • 1.接口简介
    • 2.接口的定义
    • 3.接口的实现(实现类)
    • 4.接口间的关系
    • 5.强制转换
    • 6.接口的作用
    • 7.接口回调
    • 8.抽象类与接口区别
    • 9.继承与实现的区别
  • 八、常用类
    • 1.局部内部类
    • 2.匿名内部类:
      • 特点
  • 九、Object类
    • 方法
      • hashcode()方法
      • getClass()方法
      • getName()方法
      • toString()方法
    • ==与equals区别
  • 十、包装类
      • 基本类型与包装类的相互转化
        • 基本类型转为包装类 如int为例
        • 包装类转化为基本类型
      • String与Integer相互转化
        • String--->Integer
        • Integer--->String
      • int 与String相互转换
        • int --->String,字符串拼接
        • String --->int
      • 包装类
        • 包装类的自动拆箱
        • 包装类的应用
  • 十一、String
    • 1.定义String对象
    • 2.String常用方法
    • 3.字符串常量池
    • 4. 字符串不变性
      • 字符串不变性
      • equals和==
    • 5.String、StringBuffer、StringBuilder的区别
    • 6. String str=new String("abc")创建多少个对象?
    • 7.字符串池的优缺点:
    • 8.总结
  • 十二、集合
    • 1.list
      • list的方法
      • list的遍历
      • Arraylist
      • Vector不常用
      • Linkedlist链表
    • 2 .set
      • set实现类
  • 3.Map
    • 3.1 Map中的常用方法:
      • 内部类Entry
    • 3.2 实现类
      • hashmap:
      • hashtable
      • linkedhashmap
      • treemap
      • properties
    • conCurrentHashMap、Hashmap、Hashtable区别
      • HashMap
      • HashTable
      • ConcurrentHashMap
        • 自己的笔记
    • 遍历
      • 3.3 键遍历
      • 值遍历
      • 键值对遍历
    • 3.4 迭代器遍历
      • 值遍历
      • 键遍历
      • 键值对遍历
    • 3.5 红黑树
      • 红黑树 自平衡,靠:左旋、右旋和变色。
        • 左旋
        • 右旋
      • 变色:红变黑、黑变红
  • 十三、异常
    • 1.分类 throwable
    • 2.Error和Exception区分:
    • 3.RunTimeException和其他Exception区分:
    • 4.异常的产生
    • 5.异常的传递
    • 6.异常处理
    • 7.自定义异常
  • 十四、多线程
    • 1.线程的介绍
    • 2、线程的创建
      • 继承Thread
      • 实现Runnable
      • Callable与future
      • 线程池
    • 3.线程的状态、方法
      • 线程的状态
      • 线程的方法
    • 4. 线程同步-synchronized
      • synchronized同步方法
      • synchronized同步代码块、
        • 同步静态方法-类的class对象锁
        • 同步非静态方法-类的对象锁
        • 其他资源的锁
      • 死锁、线程的通信
        • 线程的通信
    • 5.线程同步-lock
      • ReentrantLock-效率低
      • ReentrantReadWriteLock读写锁
        • 读写互斥 代码
    • 6.单例模式
      • 饿汉式
      • 懒汉式
      • 其他方法 实现懒汉模式的多线程单例
        • DCL机制
        • 静态内置类
        • static代码块
        • enum枚举
        • 序列化与反序列化
    • 7. 反射
      • 获取反射中的Class对象
      • 通过反射创建类对象
      • 通过反射获取类属性、方法、构造器
      • 类对象与类的对象的区别
    • 8.线程池
      • ExecutorService接口
      • 四种常见的线程池
    • 9. ThreadLocal 同一线程数据共享
    • 10. 异步编排
      • 创建异步对象
      • 计算完成时回调方法
      • 线程串行化方法
      • 多任务组合
      • 代码示例
  • 十五、I/O流
  • 十六、区别
    • 1. String、StringBuffer、Stringbuild区别
    • 2.ArrayList、vector、linkedList
    • 3. ==与equals区别
    • 4. &与&& 区别
    • 5. 怎么跳出多层循环?
    • 6.HashMap、HashTable、ConcurrentHashMap区别
    • 7.final、finally、finalize区别
    • 8.sleep、wait
    • 9. 重写、覆盖区别
    • 10.error、Exception区别
    • 11.同步、异步
    • 12.事务的传播机制
    • 13.事务的四大特点
    • 14.Spring的两种动态代理:Jdk和Cglib 的区别和实现
  • 十七、数据库
    • 触发器
    • 简单查询
    • 排序 order by
    • dual、sysdate、systimestamp
    • 函数
    • 分组过滤-having、group by
      • having与where区别
    • 条件查询
    • 伪列
    • 表连接

一、java特点

1.java特点

跨平台、简单(比C++而言)、纯面向对象

2. java运行机制

先编译,后运行

3.书写规范、标识符

书写规范:一行代码占一行,有缩进

标识符:

  • 数字、字母、下划线、$组成。(数字不可开头)

  • 区分大小写

  • 不可用关键字、保留字(goto、contact等)

  • 包名全小写,其他使用驼峰命名法(如类名,方法名)

  • 常量全大写,用下划线连接。如A_B

4.注释

// 行注释
/* / 为段注释
/
* */ 为文档注释

5.转义字符

’ ," , \ , \t ,\n

6.扫描器

扫描器:从键盘输入数据。

Scanner sc= new Scanner(System.in);
int  a=sc.netInt();

char r=sc.next();.charAt(0);

String s= sc.next();
其他与int方式一样。sc.next_()

7.引用变量

字符串:字符串不变性。字符串拼接,就是在堆中创建新的字符串。分StringBuffer和StringBuilder
数组:
对象:

String,StringBuffer和StringBuilder区别

和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。

StringBuffer 的方法是线程安全的(不能同步访问),StringBuilder 不安全
StringBuilder 比 StringBuffer 执行速度快,建议使用 StringBuilder 类。在要求线程安全的情况下,则必须使用 StringBuffer 类。

(1)如果要操作少量的数据用 String;
(2)多线程操作字符串缓冲区下操作大量数据 StringBuffer;
(3)单线程操作字符串缓冲区下操作大量数据 StringBuilder;

二、if、while、for

1、if 判断

if(布尔表达式),
布尔表达式:就是true,false。或者为判断语句,如1>2等

2、while 循环

while(变量或表达式)
如while(true),while(1),while(1>0)

while与do-while区别

while循环次数不一定
do-while,循环次数一定。先执行,后判断。执行次数多一次

while{
	...
}

do{
	...
}while(判断语句)

3、for 循环

格式

for(int i=0;i<n;i++){
	...
}

4、其他

a++与++a区别

a++:先使用,后a加1
++a:a先加1,后使用

局部变量、实例变量的区别

局部变量:在方法内有效,必须先赋值,后使用。命名不可冲突
实例变量:类中有效。与局部变量命名冲突,局部变量优先。

for,break,continue区别

for:for循环
break:结束循。
continue:结束本次循环,继续执行下次循环。

for的嵌套 ***

函数(方法)

结构:

函数的声明与实现
访问修饰符区别
参数–重载、覆盖(重写)

访问修饰符区别

public,default、protected、private


public是公共的,被public所修饰的成员可以在任何类中都能被访问到。
protected是受保护的,受到该类所在的包所保护。
default是对同一package的类友好。
private是私有的,即只能在当前类中被访问到,它的作用域最小。

重载、覆盖的区别

重载:方法与方法之间。方法名相同,参数列表不同
覆盖:继承关系。父子类的方法,子类对父类的扩展。方法名、参数列表相同

定义区别:

  • 重载是指不同的函数使用相同的函数名,但是函数的参数个数或类型不同。调用的时候根据函数的参数来区别不同的函数。
  • 覆盖(也叫重写)是指在派生类中重新对基类中的虚函数(注意是虚函数)重新实现。继承关系。即函数名和参数都一样,只是函数的实现体不一样。

类的关系区别

  • 覆盖是子类和父类之间的关系,是垂直关系;
  • 重载是同一个类中方法之间的关系,是水平关系。

产生方法区别

  • 覆盖只能由一个方法或只能由一对方法产生关系;
  • 重载是多个方法之间的关系。

参数不同

  • 覆盖要求参数列表相同。
  • 重载要求参数列表不同

递归

递归:方法自己调用自己。

  • 必须有收敛条件。否则会死循环。
  • 必须有返回值

三、数组

数组是引用变量,存在堆中。

1.数组的定义

int a[], a= new int[3]
int a[]=new int[3]
int a[]=new int[]{}
或者int a[]={}

堆、栈区别

数组是引用变量,存在堆中。

栈:存局部变量
堆:存引用变量。

引用变量指 字符串、对象,数组、

两个数组,如a,a2
a2=a,
如果堆中值变化,两个数组都变化。

2.数组的扩容

一般扩容为2倍

  • 老数组复制给新数组。新数组的长度比老数组大
  • System.arraycopy(原数组,原数组下标,新数组,新数组下标,老数组长度)
    old=new ,遍历老数组
  • 用copyof
    int[] arr2=new int[arr1.length*2]   //新数组长度

        for(int i=0;i<arr1.length;i++){     //复制

            arr2[i]=arr1[i];
        }

    int[] arr2=java.util.Arrays.copyOf(原数组名,新数组长度);

    int[] arr2=new int[arr1.length*2]
        System.arraycopy(原数组名,起始下标,新数组名,起始下标,复制长度);

可变长参数

三个点,表示多个参数

function(int ...a)

可变参,放在参数最后一个,且只有一个

3.数组的插入

先移位,后赋值。

for(int i=a.length-1;i>pos;i--){
	a[i]=a[i-1],移位
}
赋值:
b[pos] =value
a=b,指针指过去

4.选择、冒泡(排序)

选择、冒泡为嵌套for循环

选择排序法

i: i=0;i<a.length-1;i++
j: j=i;j<a.length-1;j++

冒泡排序法

i: i=0;i<a.length-1;i++
j: j=0;j<a.length-1-i;j++

        for (int i = 0; i < arr.length; i++) {
            //外层循环,遍历次数
            for (int j = 0; j < arr.length - i - 1; j++) {
                //内层循环,升序(如果前一个值比后一个值大,则交换)
                //内层循环一次,获取一个最大值
                if (arr[j] > arr[j + 1]) {
                    int temp = arr[j + 1];
                    arr[j + 1] = arr[j];
                    arr[j] = temp;
                }
            }
        }

系统排序法

java.util.Arrays.sort(a),a为数组名称

四、面向对象

1.java的发展过程

面向过程:自上而下,逐步求精
面向对象
APl(高级编程)

2.面向对象

属性:特征,对象有什么。数据库表的字段(列)
方法:对象可做是什么
对象的属性,也可以为对象

构造方法,局部变量、实例变量。。封装、继承、动态。

类、方法的结构

类的结构
public class{}
方法的结构
public static void 函数名(参数)

重载、覆盖

重载:overloading,方法间(可以是子类继承父类的方法,也可以是不是父类的方法),方法名相同,参数列表不同(参数个数、类型、类型排序不同),有返回值无关
覆盖:重写,overriding,父子类之间。

  • 方法名、参数列表与分类相同。
  • 子类方法的访问修饰符大于或大于分类
  • 子类方法不能比分类抛出更多异常

局部变量、实例变量的区别

局部变量:在方法内有效,必须先赋值,后使用。命名不可冲突
实例变量:在类中有效。与局部变量命名冲突,局部变量优先。

构造方法

方法名与类名一致
无返回值
只能调用一次,不可以手动调用。(一个对象调用一次)
构造方法无静态
访问修饰符 class 类名
如 public class a{}

类加载

当JVM第一次使用一个类时,会根据类的classpath(类路径)找到类的.class文件,获取类的所有信息并保存,并且创建对象。这个过程叫类加载。
类加载只执行一次

类加载的时机

类加载的时机

  • 第一次通过类名 访问静态成员时。(静态成员指 静态属性和静态方法)
  • 第一次创建对象
  • 第一次创建子类对象。(先进行父类的类加载,再执行子类的类加载)

类加载发生在所有程序执行之前。
可以做一些操作。把某些程序放在最前面。

所有类变量,在类加载时,分配空间,并初始化。

对象的创建过程(实例变量三次赋值机会)

分配空间:为属性赋默认值
属性初始化:赋初始值
调用构造方法:第三次赋值

3、this, supper使用

this, supper只能使用一个,supper必须放在构造方法的第一行

this调用属性、方法名

this调用属性:this.name
this调用方法:this.a()
this调用构造方法: this(),this(xx)

this(),this(xx)区别

this()调用无参构造方法,默认提供。
this(xx):调用有参构造方法。有this(XX),this()就无效了,不默认提供了。

supper的使用

super在子类构造方法中使用,调用父类的构造方法
super(),super(xx)

this与supper的区别

this调用自己的构造方法,supper调用父类的构造方法,都可以调用属性、方法
supper默认提供

五、面向对象三大特性

封装、继承、多态

1.封装

封装就是private,私有封装,属性私有化,必须通过get、set方法才能访问

封装了属性,通过后门的通道来访问

使用:
form表单,在controller直接私有表单,直接可以使用表单,即私有的属性

2. 继承

关键词extends
如 class Dog extends Animal,狗继承动物,dog是子类,animal是父类

重载与覆盖

重载:出现在方法间,方法可以是不继承的方法,也可以是继承父类的方法。

  • 多个方法的方法名相同,参数列表不同

覆盖:出现在子类,必须继承关系。

  • 子类的方法名、参数列表与父类完全相同。
  • 子类的访问修饰符大于或大于父类
  • 子类不能比父类抛出更多异常。

继承特点

java都是单继承,C++才有单继承。单继承,一个子类只有一个父类。可以使用接口来实现类的扩展,即类似于实现多继承的功能。
一个类可以有多个间接父类,仅仅只有一个直接父类。
注意:构造方法不可继承,属性、方法可以继承。

this、supper区别

this(),this(XX)
supper(),supper(XX)
supper调用父类构造方法,this调用自己构造方法,均可以调用属性、方法
supper在子类使用,必须为构造方法的有效第一行。
supper与this只能存在一个,系统默认提供supper
覆盖父类的属性叫遮盖

覆盖父类的方法叫覆盖,即重写

3.多态

子类对象放入父类引用。或者 父类类型的引用指向子类类型的对象。
如 Animal a=new Dog();
Animal是父类的引用,Dog是子类,父类引用指向子类对象

多态三句话

对象类型不变(变化的是引用对象)
无覆盖,只能调用父类方法
有覆盖,调用子类方法

强转转换

父->子
Animal a=new Dog();
强制转换
Dog d=(Dog)a;

子–>父 (多态)
使用instance of 判断是否转换成功,true表示子转父可以强制转换,否则强制转换会失败
Dog d=new Dog();
if(a instance of Dog()){
Animall aa=d;
}

多态使用的位置

数组,参数列表,返回值

元素:父类+ 所有子类类型
返回值实际对象: 父类+ 所有子类类型
实参:父类+ 所有子类类型

多态作用

屏蔽子类间的差异
提高代码的灵活性

六、三大修饰符 abstract、static、final

abstract、static、final。抽象,静态、最后一个

1.abstract 抽象

语法

抽象方法:

访问修饰符 abstract 返回值 方法名 (参数列表);
如 public abstract void aa();

抽象方法特点: 只有声明,没有实现

抽象类

public abstract class 类名 {
}
看如下代码:
//员工

public abstract class Employee{
	public abstract void work();//抽象函数。需要abstract修饰,并分号;结束
}

抽象类无法直接创建对象,只能被子类继承后,创建子类对象。

不能使用new 创建对象
有构造方法,创建子类对象,先创建分类对象
可以有普通成员(属性、方法)

如子类继承抽象类,必须实现抽象类的所有抽象方法,且子类是抽象类
若子类不是抽象方法,不用全部实现全部抽象方法

注意:
抽象类的方法 不一定 都是抽象方法
抽象方法的类 一定 是抽象类

抽象类常见疑惑

抽象一定是个父类,因为抽象类是不断抽取共性需求而来的。
抽象类中是可以不定义抽象方法的,此时仅仅是不让该类创建对象,用于某些特殊的设计需要。
设计时由具体类抽取出抽象类,而开发阶段应该先定义抽象父类,再根据不同需求由父类定义子类

2. static 静态

静态属性、静态方法

静态属性

静态属性:类变量

语法:static 数据类型 变量名
如 static int a;

特点:全局共有 (全局共享)
使用属性:
类名.静态属性名
如 MyClass.a;
MyClass为类名,a为静态属性名

静态方法

语法:
访问修饰符 static 返回值类型 方法名 (参数列表)
如 public static void hello(int a){}
注意;访问修饰符 static 可以换位

使用方法:
类名.方法名
如 MyClass.hello();

特点

允许使用类名进行调用属性、方法
静态方法,只能调用静态成员
非静态方法,可以调用静态方法
可覆盖,无多态

静态方法的类必须是静态类,静态类可有非静态方法

动、静态代码块–类加载

动态代码块(了解)

结构:
在类中使用 { }
执行时机:创建对象的第二步,初始化属性时,执行它

静态代码块

被static修饰的代码块
结构:
static { }
定义位置:类内,方法外
执行时机:类加载时,执行它

类加载

当JVN第一次使用类,会根据Classpath(类路径)找到类的字节码文件(.class文件),获取类的所有信息并保存。这过程叫类加载。
类加载会创建对象

类加载只执行一次

类加载时机

  • 第一次通过类名访问静态成员
  • 第一次创建对象
  • 第一次创建子类对象。先创建父类的类加载,再进行子类的类加载
类加载的使用

类加载 发生在 所有程序执行之前。
(开发时,把一写代码放入静态代码块,可以先执行)
所有类变量在类加载时,分配空间并初始化

3.final

final 表示最后一个,一定没有子类,不能被继承

final的属性–>常量

final修饰变量,为常量

  • 常量一旦赋值,不能改变,进赋值一次

final修饰成员变量(属性)

  • 无默认值。
  • 赋值时机:初始化构造中

final修饰类变量(静态变量,被static修饰的变量)

  • 赋值时机:声明并赋值,在静态代码块

final修饰的数据类型,数组不变
final修饰的引用数据类型,指向对象(地址)不变,但对象属性值可变。(final修饰的引用,引用不变,引用指向的对象的值可以变)

final C c =new C();
c.a=10; a.b=20;

final的方法

无覆盖,(无子类。因为不能被继承,所以final的方法不能被覆盖)

final的类

不能被继承(无子类)

private、final、static与abstract的关系

abstract可以覆盖、多态;

private、final、static不能与abstract连用
private :因为private 方法不能覆盖。所以不能与abstract连用
final:final无子类。无法与abstract连用
static:子类可以覆盖,没有动态。但final可以动态。

七、接口

继承为单继承,类与类之间,子类继承的方法为基本方法。怎么扩展方法?使用接口
接口:一个类可以有多接口,类与接口之间。类通过实现接口,来扩展方法,实现“多继承”功能

1.接口简介

接口是一种标准,是使用者与实现者都遵循的一种约定。接口是特殊的抽象类

2.接口的定义

结构: interface 接口名
如 interface People{ }

接口生成 .class文件 ???

可以声明引用,不能创建对象,无构造方法
属性:公开、静态、常量(public static finall 隐式的)
方法:公开、抽象的(public abstract ,方法没有方法体,隐式的)

3.接口的实现(实现类)

接口的子类被称为实现类。

类通过implements 实现接口
如 public class Dog implements Animal{ }
Dog 类名,Animal是接口名

实现类必须实现接口的所有方法。(否则实现类为抽象类)

方法:public(动态)
使用:

  • 接口的引用类型实现实现类的对象。或者 子类对象放入接口的引用(多态,与继承的多态很像)

4.接口间的关系

接口与接口之间多继承(一个接口可以有多个父接口)
一个类可以继承类,可以实现多个接口

5.强制转换

2个引用相互转换,看应用类型的实际类型是否一致。(引用指向的对象是否一致)
如 黑狗 实现Animal,黑狗 实现Dog
animal与Dog可以转换

6.接口的作用

扩展子类功能,(使用接口做一些共性的功能)
主功能调用定义在父类,次功能定义在接口

解耦合

7.接口回调

实现 使用者、实现者分离

8.抽象类与接口区别

接口是特殊的抽象类
定义方法:

  • 接口:interface,
  • 抽象:abstract

属性:

  • 接口:公开、静态、常量;
  • 抽象: 无

方法:

  • 接口:公开、抽象。
  • 抽象:无,方法未实现

都不可创建对象,接口无构造方法

实现:

  • 接口: implements
  • 抽象:无实现

9.继承与实现的区别

继承是类与类之间,实现是类与接口之间
继承是单继承,实现可以多实现

方法:

  • 继承:不必全继承
  • 实现:全部实现(否则,实现类为抽象类)

八、常用类

成员内部类、静态内部类、局部内部类、匿名内部类(***)
成员内部类:类中类
静态内部类:类中静态类
局部内部类:类中方法内的类
匿名内部类:没有名称的类中类

1.局部内部类

局部内部类:类中方法内的类

public class Test {
    public void fun(){
        final int num = 100;
        class Fun{
            private void fun2(){
                System.out.println(num);
            }
        }
        Fun fun = new Fun();
        fun.fun2();
    }
}

可以访问所在方法的局部变量,但局部变量都是final的

2.匿名内部类:

匿名内部类:没有名称的类中类

定义结构:
Person p=new Person(){};

调用匿名内部类

m2§=m2(new Person(){})

HelloWorld frenchGreeting = new HelloWorld() {
   String name = "tout le monde";
   public void greet() {
         greetSomeone("tout le monde");
   }
   public void greetSomeone(String someone) {
        name = someone;
        System.out.println("Salut " + name);
   }
 };

特点

不可以定义如何静态成员(静态属性、静态方法)
它的方法不能抽象
必须实现接口或者抽象父类的所有抽象方法
不能有构造方法(没类名)
访问外部类的成员(变量、方法),必须static
可以访问外部类的私有成员(变量、方法)

九、Object类

Object类是所有类的父类(直接或间接),它是超类。(它的任何应用、方法,任何对象都可用)

方法

hashcode()方法

public int hashCode();返回该对象的哈希码值。
默认情况下,该方法会根据对象的地址来计算
不同对象的 hashCode() 的值一般是不相同。但是,同一个对象的hashCode() 值肯定相同

getClass()方法

public final Class getClass()

返回此 Object 的运行时类。
返回对象引用的实际对象类型

getName()方法

public String getName()

可以通过 Class 类中的一个方法,获取对象的真实类的全名称。

toString()方法

public String toString()
返回该对象的字符串表示。

==与equals区别

==比的是地址

equals:比的是内容
如 字符串使用equals进行比较

==比较基本数据类型,也可以比较引用数据类型,
基本数据类型比较的是,引用数据类型比较的是地址值

equals必须重写,否则与==没有区别

equals() 方法是一个方法,只能比较引用数据类型,所有的对象都会继承 Object 类中的方法
没有重写 Object 类中的 equals 方法,equals方法和==号比较引用数据类型无区别,重写后的equals方法比较的是对象中的属性。

十、包装类

包装类,就是能够直接将简单类型的变量表示为一个类,在执行变量类型的相互转换时,我们会大量使用这些包装类。

基本类型与包装类的相互转化

以int为例

基本类型转为包装类 如int为例

new 、valueOf
int a=10;

Integer i =new Integer(a);
或者
Integer i2 =Integer.valueOf(a)

包装类转化为基本类型

xxValve,xx为基本类型
如 Integer转化为int
int a1= i.intValue();

String与Integer相互转化

String—>Integer

valueof、new
String s=“123”;

Integer I1=new Integer(s)
或者
Integer I2=Integer.valueof(s);

Integer—>String

toString
String s1=I1.toString();

int 与String相互转换

int —>String,字符串拼接

int a=10;
字符串拼接
String ss=a+" ";

String —>int

parseInt
int i3 =Integer.parseInt(s);

包装类

包装类的自动拆箱

直接使用包装类,会把它转化为基本类型
自动拆箱:默认使用valueOf()方法
Integer对-128到127的数据,做了预包装,存放在常量池,提高效率。
如数据不在此范围,就慢

包装类的自动拆箱
   int i = 128;
   Integer i2 = 128;
   Integer i3 = new Integer(128);
   System.out.println(i == i2); //Integer会自动拆箱为int,所以为true
   System.out.println(i == i3); //true,理由同上
   Integer i4 = 127;//编译时被翻译成:Integer i4 = Integer.valueOf(127);
   Integer i5 = 127;
   System.out.println(i4 == i5);//true
   
因为对象不一样,所以为falseInteger i6 = 128;
   Integer i7 = 128;
   System.out.println(i6 == i7);//false,不在-128到127之间
   Integer i8 = new Integer(127);
   System.out.println(i5 == i8); //false
   Integer i9 = new Integer(128);
   Integer i10 = new Integer(123);
   System.out.println(i9 == i10);  //false

关键就是看valueOf()函数了,这个函数对于-128到127之间的数,会进行缓存, Integer i4 = 127时,会将127进行缓存,下次再写Integer i5 = 127时,就会直接从缓存中取,就不会new了。所以i4和i5比是true,而i6和i7比是false。

而对于后边的i5和i8,以及i9和i10,因为对象不一样,所以为false。

总结:
1,无论如何,Integer与new Integer不会相等。不会经历拆箱过程,new出来的对象存放在堆,而非new的Integer常量则在常量池(在方法区),他们的内存地址不一样,所以为false。

2,两个都是非new出来的Integer,如果数在-128到127之间,则是true,否则为false。
因为java在编译Integer i2 = 128的时候,被翻译成:Integer i2 = Integer.valueOf(128);而valueOf()函数会对-128到127之间的数进行缓存。

3,两个都是new出来的,都为false。还是内存地址不一样。

4,int和Integer(无论new否)比,都为true,因为会把Integer自动拆箱为int再去比。

包装类的应用
  • 区别0零与null区别
  • 区别有效数据、无效数据

基本类型:值
包装类:对象

基本数据类型与包装类的区别
默认值:

  • 基本类型:自己的默认值,如int 为0
  • 包装类为null

数据类型:基本数据类型与对象数据类型
内存:基本小,包装:大

为什么需要包装类?
便于Object类统一管理
区分0与null

十一、String

String字符串,不变性,它是对象,引用类型(包含字符串、数组、引用)

String类是final类,也即意味着String类不能被继承,并且它的成员方法都默认为final方法
String类其实是通过char数组来保存字符串的。
String对象一旦被创建就是固定不变的了,对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象

1.定义String对象

字面形式:
String s1=“abc”

构造方法:
String s2=new String(“abc”);

字符串为引用类型,比较使用equals,比的是内容
== 比的是堆空间地址

new 创建的字符串对象,一个在串池、一个在堆空间。地址不同,不能用==,需要使用equals,比的是内容

2.String常用方法

chartAt、toCharArray、indexOf、trim、split、toUppercase、toLowerCase、endwith、contain
截取
subString

3.字符串常量池

每当创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么直接返回常量池中的实例引用。如果不存在,就会实例化该字符串并且将其放到常量池中。
由于String字符串的不可变性,常量池中一定不存在两个相同的字符串

Java中的常量池,实际上分为两种形态:静态常量池运行时常量池
静态常量池:即*.class文件中的常量池,class文件中的常量池不仅仅包含字符串(数字)字面量,还包含类、方法的信息,占用class文件绝大部分空间。
运行时常量池:则是jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在方法区中,常说的常量池,就是指方法区中的运行时常量池。

String a = "chenssy";
String b = "chenssy";
a、b和字面上的chenssy都是指向JVM字符串常量池中的"chenssy"对象,他们指向同一个对象。

String c = new String("chenssy");

a==b,为true,他们指向同一个对象

  • 采用new关键字新建一个字符串对象
    String str3=new String("aaa");
    String str4=new String("aaa");
    System.out.println("===========test2============");
    System.out.println(str3==str4);//false 可以看出用new的方式是生成不同的对象 

因为new关键字创建对象时,每次new出来的都是一个新的对象,也即是说引用str3和str4指向的是两个不同的对象

  • 编译期确定
    String s0="helloworld";
    String s1="helloworld";
    String s2="hello"+"world";
    System.out.println("===========test3============");
    System.out.println(s0==s1); //true 可以看出s0跟s1是指向同一个对象 
    System.out.println(s0==s2); //true 可以看出s0跟s2是指向同一个对象 

s0和s1中的"helloworld”都是字符串常量,它们在编译期就被确定了,所以s0s1为true;
而"hello”和"world”也都是字符串常量,当一个字符串由多个字符串常量连接而成时,它自己肯定也是字符串常量.所以s2也在编译期就被解析为一个字符串常量,所以s2也是常量池中"helloworld”的一个引用。所以我们得出s0
s1==s2。

  • 编译期无法确定
    String s0="helloworld"; 
    String s1=new String("helloworld"); 
    String s2="hello" + new String("world"); 
    System.out.println("===========test4============");
    System.out.println( s0==s1 ); //false  
    System.out.println( s0==s2 ); //false 
    System.out.println( s1==s2 ); //false

用new String() 创建的字符串不是常量,不能在编译期就确定,所以new String() 创建的字符串不放入常量池中,它们有自己的地址空间。

4. 字符串不变性

字符串不变性

  • String类初始化后是不可变的

  • 引用变量与对象

  • 使用String不一定创建对象。
    如String a = “123”
    如String a = “123”,JVM会先到常量池里查找,如果有的话返回常量池里的这个实例的引用,否则的话创建一个新实例并置入常量池里。
    对象可能并没有被创建!而可能只是指向一个先前已经创建的对象。

  • 使用new String,一定创建对象

  • new的字符串保存在堆中
    在执行String a = new String(“123”)的时候,首先走常量池的路线取到一个实例的引用,然后在堆上创建一个新的String实例

  • String.intern()
    intern方法使用:一个初始为空的字符串池,它由类String独自维护。
    当调用 intern方法时,如果池已经包含一个等于此String对象的字符(用equals(oject)方法确定),则返回池中的字符串。否则,将此String对象添加到池中,并返回此String对象的引用。

它遵循以下规则:对于任意两个字符串 s 和 t,当且仅当 s.equals(t) 为 true 时,s.intern() == t.intern() 才为 true。

equals和==

对于==,如果作用于基本数据类型的变量(byte,short,char,int,long,float,double,boolean ),则直接比较其存储的"值"是否相等;
如果作用于引用类型的变量(String),则比较的是所指向的对象的地址(即是否指向同一个对象)。

equals方法。在Object类中,equals方法是用来比较两个对象的引用是否相等,即是否指向同一个对象。

对于equals方法,注意:必须重写equals方法
equals方法不能作用于基本数据类型的变量。如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;而String类对equals方法进行了重写,用来比较指向的字符串对象所存储的字符串是否相等。其他的一些类诸如Double,Date,Integer等,都对equals方法进行了重写用来比较指向的对象所存储的内容是否相等。

5.String、StringBuffer、StringBuilder的区别

String不变性,拼接字符串就是创建新字符串。
可以使用StringBuffer、StringBuilder进行字符串拼接,不创建新字符串

  • 可变与不可变:String是不可变字符串对象,StringBuilder和StringBuffer是可变字符串对象(其内部的字符数组长度可变)

  • 多线程安全:String线程线程安全、StringBuffer 线程安全,StringBuilder不安全
    StringBuffer安全、执行限流慢,StringBuilder不安全、执行效率快。
    String中的对象是不可变的,可理解为常量,显然线程安全。StringBuffer 与 StringBuilder是等价的,StringBuffer 中的方法synchronized修饰,线程安全,而 StringBuilder 没修饰,非线程安全。

  • 当字符串相加操作或者改动较少的情况下,建议使用 String str="hello"这种形式;
    当字符串相加操作较多的情况下,建议使用StringBuilder,如果采用了多线程,则使用StringBuffer。

6. String str=new String(“abc”)创建多少个对象?

new只调用了一次,也就是说只创建了一个对象。即在堆上创建了"abc"对象

  • 该段代码执行过程和类的加载过程有区别。
    类加载的过程中,确实在运行时常量池中创建了一个"abc"对象,而在代码执行过程中确实只创建了一个String对象。

7.字符串池的优缺点:

优点:避免了相同内容的字符串的创建,节省了内存,省去了创建相同字符串的时间,同时提升了性能;
缺点:牺牲了JVM在常量池中遍历对象所需要的时间,不过其时间成本相比而言比较低。

8.总结

String 为不可变长字符串,串池被final。字符串拼接,会创建新字符串
StringBuffer、StringBuilder为可变长字符串,进行字符串拼接,不会创建新对象。

StringBuffer线程安全,执行效率慢;StringBuilder不安全,执行效率快。
使用append进行字符串拼接,相当于+

十二、集合

Collection、map

1.list

参照

list的方法

void add(int index, E element),在指定位置插入元素,后面的元素都往后移一个元素。
boolean addAll(int index, Collection<? extends E> c)

E get(int index),返回list集合中指定索引位置的元素

int indexOf(Object o)
返回list集合中第一次出现o对象的索引位置,如果list集合中没有o对象,那么就返回-1

E remove(int index),删除指定索引的对象

E set(int index, E element),在索引为index位置的元素更改为element元素

List subList(int fromIndex, int toIndex)
返回从索引fromIndex到toIndex的元素集合,包左不包右

list的遍历

for 循环

 for (int i = 0; i < list.size(); i++) {
       Strings = list.get(i);
       System.out.println(s);
 }

for-each

  for (String s : list) {
          System.out.println(s);
  }

迭代器
根据List集合的自动遍历

 Iterator<News> it = list.iterator();
 while (it.hasNext()) {
     Object o =  it.next();
     System.out.println(o);
}

Arraylist

参考
有下标,

Vector不常用

Linkedlist链表

参照

LinkedList类是双向列表,列表中的每个节点都包含了对前一个和后一个元素的引用

查询慢,添加、删除快

2 .set

无序、无下标、元素不可重复
方法:全部继承父接口Collection的方法

set实现类

  • hashset(实现类):
    遍历:for-each
    基于hashcode实现元素内容不重复。
    当存入元素的hashcode相同时,使用equals比内容是否相同。所以必须重写hashcode、equals、toString

  • sortedset:
    它为 set的接口,将集合升序
    它的实现类为treeset,treeset自动排序

    • 当元素为自定义对象类型时,需要实现Compareable接口,重写CompareTo方法
    • 当返回值为0,代表元素内容相同,拒绝添加
  • linkedhashset: 它是的子类,使用双向链表来维护键值对的次序
    元素添加、遍历顺序一致

3.Map

参考
键值对

Map用于保存具有映射关系的数据,Map集合里保存着两组值,一组用于保存Map的ley,另一组保存着Map的value。

key不能重复,无序,无下标;value无下标,无序,可以重复

3.1 Map中的常用方法:

V get(Object key) 返回 Map 集合中指定键对象所对应的值。V 表示值的数据类型
V put(K key, V value) 向 Map 集合中添加键-值对,返回 key 以前对应的 value,如果没有, 则返回 null
V remove(Object key) 从 Map 集合中删除 key 对应的键-值对,返回 key 对应的 value,如果没有,则返回null
Set entrySet() 返回 Map 集合中所有键-值对的 Set 集合,此 Set 集合中元素的数据类型为 Map.Entry
Set keySet() 返回 Map 集合中所有键对象的 Set 集合
boolean containsKey(Object key):查询Map中是否包含指定的key值;
boolean containsValue(Object value):查询Map中是否包含一个或多个value;
size,获取键值对的个数

内部类Entry

Map中包括一个内部类Entry,该类封装一个键值对,常用方法:

Object getKey():返回该Entry里包含的key值;
Object getvalue():返回该Entry里包含的value值;
Object setValue(V value):设置该Entry里包含的value值,并设置新的value值。

3.2 实现类

hashmap:

自定义对象,必须重写hashcode、equals方法
线程不安全,运行快,允许null(k,v)

hashtable

线程安全,运行慢,不允许null

linkedhashmap

它是HashMap的子类
保证集合遍历、添加顺序一致

和HashSet中的LinkedHashSet一样,HashMap也有一个LinkedHashMap子类,使用双向链表来维护键值对的次序,迭代顺序和插入顺序保持一致。

treemap

对key进行升序,实现compareable接口,重写CompareTo方法

TreeMap就是一个红黑树数据结构,每个键值对作为红黑树的一个节点。存储键值对时根据key对节点进行排序。可以保证所有键值对处于有序状态。
和TreeSet一样,TreeMap也有自然排序和定制排序两种排序方式。

与TreeSet类似的是,TreeMap中提供了一系列根据key顺序访问键值对的方法:

properties

通过配置文件,扫描键值对

conCurrentHashMap、Hashmap、Hashtable区别

参考

HashMap

底层数组+链表实现,可以存储null键和null值,线程不安全
初始size为16,扩容:newsize = oldsize*2,size一定为2的n次幂
扩容针对整个Map,每次扩容时,原来数组中的元素依次重新计算存放位置,并重新插入
插入元素后才判断该不该扩容,有可能无效扩容(插入后如果扩容,如果没有再次插入,就会产生无效扩容)
当Map中元素总数超过Entry数组的75%,触发扩容操作,为了减少链表长度,元素分配更均匀
计算index方法:index = hash & (tab.length – 1)

HashTable

底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,ConcurrentHashMap做了相关优化
初始size为11,扩容:newsize = olesize*2+1
计算index的方法:index = (hash & 0x7FFFFFFF) % tab.length

HashMap的初始值还要考虑加载因子:
哈希冲突:若干Key的哈希值按数组大小取模后,如果落在同一个数组下标上,将组成一条Entry链,对Key的查找需要遍历Entry链上的每个元素执行equals()比较。
加载因子:为了降低哈希冲突的概率,默认当HashMap中的键值对达到数组大小的75%时,即会触发扩容。因此,如果预估容量是100,即需要设定100/0.75=134的数组大小。
空间换时间:如果希望加快Key查找的时间,还可以进一步降低加载因子,加大初始大小,以降低哈希冲突的概率。

HashMap和Hashtable都是用hash算法来决定其元素的存储,因此HashMap和Hashtable的hash表包含如下属性:

容量(capacity):hash表中桶的数量
初始化容量(initial capacity):创建hash表时桶的数量,HashMap允许在构造器中指定初始化容量
尺寸(size):当前hash表中记录的数量
负载因子(load factor):负载因子等于“size/capacity”。负载因子为0,表示空的hash表,0.5表示半满的散列表,依此类推。轻负载的散列表具有冲突少、适宜插入与查询的特点(但是使用Iterator迭代元素时比较慢)
除此之外,hash表里还有一个“负载极限”,“负载极限”是一个0~1的数值,“负载极限”决定了hash表的最大填满程度。当hash表中的负载因子达到指定的“负载极限”时,hash表会自动成倍地增加容量(桶的数量),并将原有的对象重新分配,放入新的桶内,这称为rehashing。

HashMap和Hashtable的构造器允许指定一个负载极限,HashMap和Hashtable默认的“负载极限”为0.75,这表明当该hash表的3/4已经被填满时,hash表会发生rehashing。

“负载极限”的默认值(0.75)是时间和空间成本上的一种折中:

较高的“负载极限”可以降低hash表所占用的内存空间,但会增加查询数据的时间开销,而查询是最频繁的操作(HashMap的get()与put()方法都要用到查询)
较低的“负载极限”会提高查询数据的性能,但会增加hash表所占用的内存开销
程序猿可以根据实际情况来调整“负载极限”值。

ConcurrentHashMap

底层 采用分段的数组+链表实现,线程安全
通过把整个Map分为N个Segment,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。(读操作不加锁,由于HashEntry的value变量是 volatile的,也能保证读取到最新的值。)
Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术
有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁
扩容:段内扩容(段内元素超过该段对应Entry数组长度的75%触发扩容,不会对整个Map进行扩容),插入前检测需不需要扩容,有效避免无效扩容

Hashtable和HashMap都实现了Map接口,但是Hashtable的实现是基于Dictionary抽象类的。Java5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。

HashMap基于哈希思想,实现对数据的读写。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,然后找到bucket位置来存储值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞时,对象将会储存在链表的下一个节点中。HashMap在每个链表节点中储存键值对对象。当两个不同的键对象的hashcode相同时,它们会储存在同一个bucket位置的链表中,可通过键对象的equals()方法来找到键值对。如果链表大小超过阈值(TREEIFY_THRESHOLD,8),链表就会被改造为树形结构。

在HashMap中,null可以作为键,这样的键只有一个,但可以有一个或多个键所对应的值为null。当get()方法返回null值时,即可以表示HashMap中没有该key,也可以表示该key所对应的value为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个key,应该用containsKey()方法来判断。而在Hashtable中,无论是key还是value都不能为null。

Hashtable是线程安全的,它的方法是同步的,可以直接用在多线程环境中。而HashMap则不是线程安全的,在多线程环境中,需要手动实现同步机制。

Hashtable与HashMap另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。

类图

从类图中可以看出来在存储结构中ConcurrentHashMap比HashMap多出了一个类Segment,而Segment是一个可重入锁。

ConcurrentHashMap是使用了锁分段技术来保证线程安全的。

锁分段技术:首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。

ConcurrentHashMap提供了与Hashtable和SynchronizedMap不同的锁机制。Hashtable中采用的锁机制是一次锁住整个hash表,从而在同一时刻只能由一个线程对其进行操作;而ConcurrentHashMap中则是一次锁住一个桶。

ConcurrentHashMap默认将hash表分为16个桶,诸如get、put、remove等常用操作只锁住当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的。

自己的笔记

hashmap线程不安全,效率高
hashtable全线程加锁 保证线程安全,效率太低
ConcurrentHashMap使用了锁分段技术来保证线程安全的, 添加同步操作控制并发

1.7 与1.8的ConcurrentHashMap不同
数据结构:取消了分段锁数据结构,使用数组+链表+红黑树
保证线程安全机制:1.7分段锁,1.8 CAS(Node)+Synchronized
锁的粒度:1.7对需要的数据进行加锁,1.8对每个数组元素加锁
链表–>红黑树:

  • 定位节点hash算法有缺点,hash冲突。
  • 当链表节点大于8,将链表—>红黑树进行存储

时间复杂度:O(N)—>O(logN)

遍历

map–>hashmap

3.3 键遍历

使用set,因为set无序,无下标,不可重复
通过key获得value

Set<String> keyset =Map..keyset();
for(String key : keyset ){
	String v=map.get(key);
	println(v)
}

值遍历

使用Collection,因为value无法确定类型

Collection<String > values =Map.values();
for(String v : values ){
	println(v)
}

键值对遍历

Entry—>Set ,Entry—>entryset

Set<Map.Entry<String,String>> set map.entryset();
for(Map.Entry<String,String> entry : set){
	String k=entry.getKey();
	String v=entry.getValue();
	println(k,v)
}

3.4 迭代器遍历

参考
Map map= new HashMap();

值遍历

只有value

Collection<> vs = map.values();
Iterator<String> it =vs.iterator();
while(it.hasNext()){
	Object v=it.next();
	System.out.println(v);
}

键遍历

只有key

Set <String> ks=map.keySet();
Iterator<String> it =ks.iterator();
while(it.hasNext()){
	Object k=it.next();
	Object v=map.get(k);
	System.out.println(k+","v);
}

键值对遍历

用Entry的entryset方法

Set<Map.Entry<String,Sttring>> es=map.entrySet();
Iterator<String> it =es.iterator();

while(it.hasNext()){
Map.Entry me= it.next();//需要强转
	Object k=me.getKey();
	Object v=map.getValue(k);
	System.out.println(k+","v);
}

3.5 红黑树

参考
红黑树是一种含有红黑结点,并能自平衡的二叉查找树。它必须满足下面性质:
性质1:每个节点要么是黑色,要么是红色。
性质2:根节点是黑色。
性质3:每个叶子节点(NIL)是黑色。
性质4:每个红色结点的两个子结点一定都是黑色。
性质5:任意一结点到每个叶子结点的路径都包含数量相同的黑结点。

从性质5又可以推出:
性质5.1:如果一个结点存在黑子结点,那么该结点肯定有两个子结点

(5) 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。

红黑树 自平衡,靠:左旋、右旋和变色。

左旋只影响旋转结点和其右子树的结构,把右子树的结点往左子树挪了。
右旋只影响旋转结点和其左子树的结构,把左子树的结点往右子树挪了。

左旋

以p为旋转节点,
其右子节点V变成旋转节点的父节点,其右子节点的左子节点R变为旋转接点的右子节点,其左节点F不变

右旋

以p为旋转节点

其左子节点F变成旋转节点的父节点,其左子节点的右子节点K变为旋转接点的左子节点,其右节点V不变

变色:红变黑、黑变红

红黑树总是通过旋转和变色达到自平衡。

根据红黑树插入,进行判断
参考1
参考2

十三、异常

参考

1.分类 throwable

Throwable是Error和Exception的父类,用来定义所有可以作为异常被抛出来的类。

2.Error和Exception区分:

Error是编译时错误和系统错误,程序无法处理。
系统错误在除特殊情况下,都不需要你来关心,基本不会出现。而编译时错误,如果你使用了编译器,那么编译器会提示。

Exception则是可以被抛出的基本类型
Exception又分为RunTimeException和其他Exception。

3.RunTimeException和其他Exception区分:

  • 其他Exception,受检查异常。可以理解为错误,必须要开发者解决以后才能编译通过,解决的方法有两种,1:throw到上层,2,try-catch处理。
  • RunTimeException:运行时异常,又称不受检查异常,不受检查!不受检查!!不受检查!!!重要的事情说三遍,因为不受检查,所以在代码中可能会有RunTimeException时Java编译检查时不会告诉你有这个异常,但是在实际运行代码时则会暴露出来,
    比如经典的1/0,空指针等。如果不处理也会被Java自己处理

4.异常的产生

  • 自动产生:错误代码,抛出异常,程序停止
  • 手动产生:new (相当于return)
    throw new 异常类

5.异常的传递

沿着方法的调用链反方向传递,直至到JVM,程序终止

6.异常处理

  • 消极处理:上抛(throws)
  • 积极处理:try …catch,try…catch…finally

7.自定义异常

十四、多线程

1.线程的介绍

线程:进程的进程,分CPU时间片、数据、代码
数据:

  • 对象
    存在堆空间,堆空间共享
  • 局部变量
    局部变量存在栈空间,不共享

2、线程的创建

参考

继承Thread、实现Runnable、Callable与future、线程池

继承Thread

public class MyThread extends Thread{//继承Thread类
  public void run(){
  //重写run方法
  }
}

public class Main {
  public static void main(String[] args){
    new MyThread().start();//创建并启动线程
  }
}

实现Runnable

public class MyThread2 implements Runnable {//实现Runnable接口
  public void run(){
  //重写run方法
  }
}

public class Main {
  public static void main(String[] args){
    //创建并启动线程
    MyThread2 myThread=new MyThread2();
    Thread thread=new Thread(myThread);
    thread().start();
    //或者    new Thread(new MyThread2()).start();
  }
}

Callable与future

Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大。

  • call()方法可以有返回值
  • call()方法可以声明抛出异常

Future接口来代表Callable接口里call()方法的返回值,并且为Future接口提供了一个实现类FutureTask,这个实现类既实现了Future接口,还实现了Runnable接口

  • 创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。
  • 使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值
  • 使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)
  • 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
public static class MyThread3 implements Callable{
    @Override
    public Object call() throws Exception {
        return 5;
    }
}

public class Main {
  public static void main(String[] args){
   MyThread3 th=new MyThread3();

   //也可以直接使用Lambda表达式创建Callable对象
     //使用FutureTask类来包装Callable对象

   FutureTask<Integer> future=new FutureTask<Integer>(
    (Callable<Integer>)()->{
      return 5;
    }
    );

   new Thread(future,"有返回值的线程").start();//实质上还是以Callable对象来创建并启动线程

    try{
    System.out.println("子线程的返回值:"+future.get());//get()方法会阻塞,直到子线程执行结束才返回

    }catch(Exception e){
    e.printStackTrace();
   }
  }
}

线程池

Executor框架包括:线程池,Executor,Executors,ExecutorService,CompletionService,Future,Callable等。

newCachedThreadPool() 、newFixedThreadPool(int) 、newScheduledThreadPool(int)、newSingleThreadExecutor()

3.线程的状态、方法

线程的状态

初始状态、waiting、运行状态、timed waiting、terminaled、blocked

线程的方法

sleep
join:让别的线程加入正在执行的线程。先执行加入的线程,结束后,继续执行原来的线程
start:启动线程

4. 线程同步-synchronized

多线程抢临界资源,发生数据不安全。要使用同步锁。

synchronized同步方法

方法加synchronized

public synchronized void method()
{
   // todo
}

synchronized同步代码块、

方法内的同步代码块
this指的类的对象

public void method()
{
   synchronized(this) {
   }
}

类的class锁、类的对象锁、其他资源的锁

同步静态方法-类的class对象锁

同步锁是类的class对象,this是类的class对象

同步非静态方法-类的对象锁

同步锁是类的对象,this是类的对象

其他资源的锁

synchronized(x),X值其他资源,如对象、变量。。
只要是临界资源,多线程,必须同步

死锁、线程的通信

两个方法或者同步代码块,相互获得持有双方需要的锁,又不释放锁。会造成死锁

线程的通信

await:让线程进入等待状态,释放锁(锁标记)
notify、notifyAll:唤醒等待的一个或使用线程

5.线程同步-lock

lock是synchronized的进阶,可代替Synchronized
实现类:ReentrantLock、ReentrantReadWriteLock读写锁

ReentrantLock-效率低

对一切同步,效率很低
使用ReentrantReadWriteLock读写锁

ReentrantReadWriteLock读写锁

读锁:共享
写锁:排斥,有同步
read、write锁
lock、unlock

只有写锁,就同步

  • 在线程持有读锁的情况下,该线程不能取得写锁 (因为获取写锁的时候,如果发现当前的读锁被占用,就马上获取失败,不管读锁是不是被当前线程持有)。

  • 在线程持有写锁的情况下,该线程可以继续获取读锁 (获取读锁时如果发现写锁被占用,只有写锁没有被当前线程占用的情况才会获取失败)。

读写互斥 代码

读写的方法

public class Service {
	private ReentrantReadWriteLock lock= new ReentrantReadWriteLock();
 
	public void read (){
		try{
			try{
				lock.readLock.lock();
				System.out.println("获得读锁"+Thread.currentThread.getName());
				Thread.sleep(10000);
			}finally{
				lock.readLock.unlock();
			}
		}catch(InterruptedException  e){
			e.printStackTrance();
		}
	}
	
	public void write(){
		try{
			try{
				lock.writeLock.lock();
				System.out.println("获得写锁"+Thread.currentThread.getName());
				Thread.sleep(10000);
			}finally{
				lock.writeLock.unlock();
			}
		}catch(InterruptedException  e){
			e.printStackTrance();
		}
	}
}

run方法

public class Run{
	public static void main (String[] args ) throws InterruptedException{
		Service service =new Servcie();
		ThreadA a= new ThreadA (service);
		a.setName("A):
		a.start();
		Thread.sleep(1000);
		
		ThreadB b= new ThreadB (service);
		b.setName("B"):
		b.start();
	
	}
}

结果:读写互斥

6.单例模式

饿汉式、懒汉式

饿汉式

public classs MyObject{
    private static MyObject myobject = new MyObject();
    private MyObject(){}
    public static MyObject getInstance() {
        return MyObject;
    }
} 

但此版本代码的缺点是不能有其他实例变量,因为getInstance()方法没有同步,所以有可能出现非线程安全问题

懒汉式

public classs MyObject{
    private static MyObject myobject;
    private MyObject(){}
    public static MyObject getInstance() {
        if(myobject != null) {
        } else {
            myobject = new MyObject();
        }
       return myobject;
    }
}

缺点:多线程环境下会出现“错误的单例”创建出“多例”的情况

同步get方法,实现同步。
但是多线程下还是不能实现单例,怎么办?

其他方法 实现懒汉模式的多线程单例

DCL机制

DCL机制是大多数对线程结合单例模式使用的解决方案
使用volatile修饰变量使该变量在多个线程间达到可见性,另外也禁止了初始化的时候的代码重排序

静态内置类

类中静态类

 public class MyObject {
    private static class MyObjectHandler {
        private static MyObject myObject = new MyObject();
    }
    private MyObject(){}
    public static MyObject getInstance() {
        return MyObjectHandler.myObject;
    }
} 
static代码块
public class MyObject {
    private static MyObject instance = null;
    private MyObject(){}
    static {
        instance = new MyObject();
    }
    public static MyObject getInstance() {
        return instance;
    }
} 
enum枚举

使用枚举类时,构造方法会自动调用
类中加枚举类

序列化与反序列化

7. 反射

参考 Java反射:入门、使用、原理

通过反射获取类的信息

类对象:类加载的产物,包含类的所有信息

获取 Class 的方法 、一个 jvm 中一种 Class 只会被实例化一次

获取反射中的Class对象

  • 使用 Class.forName 静态方法
    参数是类的全类名
Class clz = Class.forName("java.lang.String");
  • 使用 类名 .class 方法。
Class clz = Student.class;
  • 使用类对象的 getClass() 方法。
String str = new String("Hello");
Class clz = str.getClass();

通过反射创建类对象

Apple是类名

第一种:通过 Class 对象的 newInstance() 方法。

Class clz = Apple.class;
Apple apple = (Apple)clz.newInstance();

第二种:通过 Constructor 对象的 newInstance() 方法

Class clz = Apple.class;
Constructor constructor = clz.getConstructor();
Apple apple = (Apple)constructor.newInstance();

通过反射获取类属性、方法、构造器

Class clz = Apple.class;
Field[] fields = clz.getFields();
for (Field field : fields) {
    System.out.println(field.getName());
}
Class clz = Apple.class;
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
    System.out.println(field.getName());
}

类对象与类的对象的区别

类对象是类加载的产物,包含类的所有信息
类的对象:通过new 创建的对象

8.线程池

参考

线程池就是首先创建一些线程,它们的集合称为线程池。
使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务。

ExecutorService接口

参考Java线程池ExecutorService
ExecutorService接口表示线程池

获取线程池
ExecutorService es =Executors.newFixedThreadPool(3);

创建多个任务对象
Runnable r1=new Task1();

添加任务到线程池
es.submmit(r1);

关闭线程池
es.shutdown();

四种常见的线程池

4种常用线程池介绍

newCachedThreadPool、newFixedThreadPool、newScheduledThreadPool、newSingleThreadExecutor
自定义线程池

9. ThreadLocal 同一线程数据共享

同一线程的数据,进行共享,让其他任务进行数据共享
Java中的ThreadLocal详解
get、set方法,remove方法

10. 异步编排

异步编排(CompletableFuture异步调用

创建异步对象

static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

runAsync方法不支持返回值。
supplyAsync可以支持返回值。

计算完成时回调方法

public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action);
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action);
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor);

public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn);

whenComplete可以处理正常和异常的计算结果,exceptionally处理异常情况。
方法不以Async结尾,意味着Action使用相同的线程执行,而Async可能会使用其他线程执行

线程串行化方法

thenApply 方法:当一个线程依赖另一个线程时,获取上一个任务返回的结果,并返回当前任务的返回值。
thenAccept方法:消费处理结果。接收任务的处理结果,并消费处理,无返回结果。
thenRun方法:只要上面的任务执行完成,就开始执行thenRun,只是处理完任务后,执行 thenRun的后续操作

带有Async默认是异步执行的。这里所谓的异步指的是不在当前线程内执行。

public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)

public CompletionStage<Void> thenAccept(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor);

public CompletionStage<Void> thenRun(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action,Executor executor); 

多任务组合

public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs);

public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs); 

allOf:等待所有任务完成

anyOf:只要有一个任务完成

代码示例

@Service
public class ArticleService {
	@Autowired
    private ArticleClient articleClient;
    @Autowired
    private UserClient userClient;
    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;
    
 	public ItemVo load(Long id) {
	// 1. 查询文章详情 0.5s
	// 下面的查询需要用到文章对应的发布用户,所以这里需要使用CompletableFuture.supplyAsync
	CompletableFuture<ArticleEntity> articleCompletableFuture = CompletableFuture.supplyAsync(() -> {
            ResponseVo<ArticleEntity> skuEntityResponseVo = this.articleClient.getArticleById(id);
            ArticleEntity articleEntity = skuEntityResponseVo.getData();
            if (articleEntity == null) {
                return null;
            }
            itemVo.setId(id);
            itemVo.setTitle(articleEntity.getTitle());
            itemVo.setDefaltImage(articleEntity.getDefaultImage());
            return articleEntity;
        }, threadPoolExecutor);
        
	// 2. 查询文章博主个人信息 0.5s
	// 这里查询需要依赖文章关联的用户id,所以需要使用articleCompletableFuture.thenAcceptAsync()
    CompletableFuture<Void> userCompletableFuture = articleCompletableFuture.thenAcceptAsync(articleEntity -> {
        ResponseVo<UserEntity> categoryResponseVo = this.userClient.queryUserInfoById(articleEntity.getUserId());
        UserEntity userEntity = categoryResponseVo.getData();
        itemVo.setUserInfo(userEntity);
    }, threadPoolExecutor);    
    
	// 3. 查询博主相关文章分类 1s
	// 这里查询需要依赖文章关联的用户id,所以需要使用articleCompletableFuture.thenAcceptAsync()
    CompletableFuture<Void> userOtherArticleCompletableFuture = articleCompletableFuture.thenAcceptAsync(articleEntity -> {
        ResponseVo<List<UserAuserOtherArticleEntity>> categoryResponseVo = this.articleClient.queryUserAuserOtherArticleById(articleEntity.getUserId());
        UserAuserOtherArticleEntity userAuserOtherArticleEntity = categoryResponseVo.getData();
        itemVo.setUserAuserOtherArticleList(userAuserOtherArticleEntity);
    }, threadPoolExecutor);
    
    // 4. 查询文章评论 1s
    // 不需要依赖其他请求返回值,可以使用新的异步对象 CompletableFuture.runAsync()
    CompletableFuture<Void> commentsCompletableFuture =  CompletableFuture.runAsync(() -> {
        ResponseVo<List<UserArticleCategoryEntity>> userArticleCategoryVo = this.userClient.queryCommentsByArticleId(id);
        UserArticleCategoryEntity userArticleCategoryEntity = userArticleCategoryVo.getData();
        itemVo.setUserArticleCategoryList(userArticleCategoryEntity);
    }, threadPoolExecutor);
    
	// 5. 相关推荐文章 1s
	// 不需要依赖其他请求返回值,可以使用新的异步对象 CompletableFuture.runAsync()
	CompletableFuture<Void> relatedArticlesCompletableFuture =  CompletableFuture.runAsync(() -> {
        ResponseVo<List<RelatedArticlesEntity>> userArticleCategoryVo = this.articleClient.queryRelatedArticles(id);
        UserArticleCategoryEntity userArticleCategoryEntity = userArticleCategoryVo.getData();
        itemVo.setUserArticleCategoryList(userArticleCategoryEntity);
    }, threadPoolExecutor);
	}
	
	// 多任务执行组合 CompletableFuture.allOf()
	CompletableFuture.allOf(articleCompletableFuture, userCompletableFuture, userOtherArticleCompletableFuture,
                commentsCompletableFuture, relatedArticlesCompletableFuture).join();
     return itemVo;
}

十五、I/O流

十六、区别

1. String、StringBuffer、Stringbuild区别

2.ArrayList、vector、linkedList

3. ==与equals区别

4. &与&& 区别

5. 怎么跳出多层循环?

6.HashMap、HashTable、ConcurrentHashMap区别

7.final、finally、finalize区别

8.sleep、wait

9. 重写、覆盖区别

10.error、Exception区别

11.同步、异步

12.事务的传播机制

  • PROPAGATION_REQUIRED
    如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。

  • PROPAGATION_SUPPORTS
    支持当前事务,如果当前没有事务,就以非事务方式执行。

  • PROPAGATION_MANDATORY
    使用当前的事务,如果当前没有事务,就抛出异常。

  • PROPAGATION_REQUIRES_NEW
    新建事务,如果当前存在事务,把当前事务挂起。

  • PROPAGATION_NOT_SUPPORTED
    以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

  • PROPAGATION_NEVER
    以非事务方式执行,如果当前存在事务,则抛出异常。

  • PROPAGATION_NESTED
    如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

13.事务的四大特点

acid
原子、一致、隔离、持久

14.Spring的两种动态代理:Jdk和Cglib 的区别和实现

1
2

java动态代理:是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

cglib动态代理:是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP

3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

如何强制使用CGLIB实现AOP?
(1)添加CGLIB库,SPRING_HOME/cglib/*.jar
(2)在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>

JDK动态代理和CGLIB字节码生成的区别?
(1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类
(2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
因为是继承,所以该类或方法最好不要声明成final

十七、数据库

触发器

数据库之——触发器
数据库触发器(trigger)的简单使用操作

简单查询

增删改查
select * from employees;
起别名 在列明前加 as,再跟着别名,as可以省略
字符串拼接 ||
条件查询 where
区间查询 between …and
null查询(is/is not)
去重 distinct
枚举查询(is /is not)
模糊查询(like、not like)%匹配多个字符,_下划线表示一个字符

排序 order by

一个排序:order by 列名
多重排序:order by 列名 and 列名。有排队顺序的

dual、sysdate、systimestamp

函数

组函数:
max,min,avg,sum,count

单行函数:
length,mod,to_char,to_date

分组过滤-having、group by

  • having:先分组,再进行条件过滤

select … from … where gropup by …having …order by
执行顺序:from ,where,group by ,having ,select,order by
如:先分组、后过滤
select department-id,avg(salary) from employees
group by department-id,
having avc(salary)>8000

having与where区别

若都可以达到相同效果,优先使用where
having过滤条件是分组后数据,where不能跟组函数

条件查询

伪列

rowid:查找数据在内存的物理地址
rownum:对满足where查询的行,进行编号,从1开始

表连接

内连接:
表名 inner join 表名
on 连接条件

左连接:
表名 left join 表名
on 连接条件

右连接
right join

本文标签: 基础知识Java