文章目录
- 1.Java基本语法
- 1.1基础
- 1.2变量和数据类型
- 1.3数组
- 2.流程控制
- 3. 面向对象
- 4.访问控制符
- 5. 异常处理
- 6.常用系统类
- 7.集合
- 8.流
- 9. 多线程
- 1.线程和进程的概念、区别
- 2.如何创建多线程:(5个)
- 3.线程的生命周期
- 4.线程的交互的方法
- 5.线程的调度的方法
- (1)定义
- (2)协同式线程调度(Cooperative Threads-Scheduling)
- (3)抢占式线程调度(Preemptive Threads-Scheduling)
- (4)Java线程优先级
- (5)Java 线程优先级与Windows线程优先级对应关系
- (6)优先级可能会被系统自行改变
- 6.线程的同步(交替打印AB)与锁(对象锁)、死锁概念(写一下)
- 7.线程的资源共享(写一下)
- 8.线程组、守护线程(了解)
- 9.Lock锁(JDK的API)
- 10.线程池的原理、API
- 11.生产者与消费者模式(原理)
- 12.单例模式
- 10. Socket
- 11.反射
- 12. XML解析
- 13.JDBC(JDBC)
1.Java基本语法
1.1基础
Java常用命令:
java(生成.class文件)/javac(运行.class文件)
Java开发平台:
J2EE(企业版)J2SE(标准版)J2ME(移动端)
Java可执行文件:
.class文件/字节码文件
Java的跨平台:
不同的机器有不同的虚拟机JVM
Java配置环境变量:
PATH 文件搜索路径,当运行一个可执行文件时,DOS会先在当前目录下寻找该文件,若找不到时,则根据PATH设置的路径寻找
CLASSPATH 可以让JAVA执行环境找到指定的Java程序(也就是.class文件)
JDK7/8/9/10区别
1.2变量和数据类型
1什么是变量
变量就是可以改变的量,与常量区分
局部变量:类的方法中的变量 在方法体中定义的变量和方法的参数称局部变量
实例变量:独立于方法之外的变量但没有static修饰
静态变量:独立于方法之外,有static修饰
2变量的命名规范(语法)和习惯(代码规范)
字母,下划线和$开头,不用中文命名(可能会有乱码),驼峰命名法(第二个词首字母大写)
3数据类型划分
基本数据类型:(1)字节的(字节型)byte,(布尔型(true和false))boolean;
(2)字节的(字符型)char,(短整型)short;
(4)字节的(整数型(也是默认数据类型))int,(浮点型(可带小数))float;
(8)字节的(长)long,(双精度(可带小数))double;
引用数据类型:除了基本数据类型的其他所有类型 例如各种类class 接口interface 数组array等
4数据类型转换
小类型转大类型:自动类型转换
int a;
long b=a;
大类型转小类型:强制类型转换
int a;
byte b=(byte)a;
int类型转char:转换为ASCII码值
char c="c";
int i=c;
输出i为99;
5Java缺省类型
整数缺省类型为int
浮点缺省为double
6内存的划分
1.栈(Stack)存放局部变量,值
2.堆(Heap)new出来的对象 (包装类,new)
3.方法区(Method Area)存放.class信息
4.本地方法区(Native Method Area)操作系统相关
5.寄存器(pc Register)CPU相关,高性能
7运算符
算数运算符:
+; -; *; /;%(取余);
i++/i–先赋值再自加/减;++i/–i先自加/减再赋值;
比较运算符:
<; >; <=; >=; ==;!=;
逻辑运算符:
&与(两个都满足为ture) &&条件与(若第一个不满足则直接false)
|或(满足一个则为ture) ||条件或(若第一个满足则直接ture)
^异或(不同为1 ture,相同为0 false)
!非
赋值运算符:
=;+=;-=;等
c+=a;等价于c=c+a;
三元运算符:
?:
例:boolean a=20<45?ture:false
等价于
boolean a;
if(20<45){
a=ture;
}else{
a=false;
}
位运算符
“<<”;左移2位
“>>”;有符号右移
“>>>”;无符号右移
“~”;取反
8运算符优先级
1.3数组
数组的声明
先声明后赋值:
int array[];
array = new int[10];
声明同时创建:
int array[] = new int[10];
直接赋值:
int array[] = new int[1,2,3,4];
数组的初始化
动态初始化:定义数组并分配空间和赋值分开进行
静态初始化:定义数组的同时就为其赋值
数组的定义,特性和遍历
定义:数组是一组相同类型的相关变量的集合,一维数组分配的空间是连续的
特性:固定长度,内部变量的类型相同,索引从array[0]开始计数
遍历:
普通for循环:
int array[] = {1,2,3,4,5};
for(int i=0;i<array.length;i++){
System.out.println(array[i]);
}
增强for循环:
int array[] = {1,2,3,4,5};
for(int x:array){
System.out.println(x);
}
多维数组
定义:可以看做是数组的数组,数组空间不是连续分配的所以不要求每一维的数组大小相同
int a[][] = new int[10][10];
int [][]a = new int[10][10];
int []a[] = new int[10][10];
遍历:
双重for循环:
int [][]a = new int[10][10];
for(int i=0;i<a.length;i++){
for(int j=0;j<a[i].length;j++){
System.out.println(a[i][j]);
}
}
增强for循环:
int [][]a = new int[10][10];
for(int[] is:a){
for(int i:is){
System.out.println(i);
}
}
2.流程控制
2.1顺序结构
按顺序运行的结构
2.2分支结构
if…else选择
if(条件){执行语句
}else{执行语句
}
if…else if
if(条件1){
执行语句
}else if(条件2){
执行语句
}
switch
switch(值){
case 1:条件1
执行语句
break;
case 2:条件2
执行语句
break;
default:
执行语句
}
2.3循环结构
普通for循环
for(条件){
执行语句
}
增强for循环
for(声明语句:表达式){
执行语句
}
whlie循环
while(布尔表达式例如x>20){
执行语句
}
do…while
do{
执行语句
}while(布尔表达式)
break跳出当前循环
continue跳转到下一次循环
2.4排序
直接插入(看一下)
直接插入排序是将未排序的数据插入至已排好序序列的合适位置。
public class InsertSort {
public static void main(String[] args) {
int a[] = {3,1,5,7,2,4,9,6,10,8};
InsertSort obj=new InsertSort();
System.out.println("初始值:");
obj.print(a);
obj.insertSort(a);
System.out.println("\n排序后:");
obj.print(a);
}
public void print(int a[]){
for(int i=0;i<a.length;i++){
System.out.print(a[i]+" ");
}
}
public void insertSort(int[] a) {
for(int i=1;i<a.length;i++){//从头部第一个当做已经排好序的,把后面的一个一个的插到已经排好的列表中去。
int j;
int x=a[i];//x为待插入元素
for( j=i; j>0 && x<a[j-1];j--){//通过循环,逐个后移一位找到要插入的位置。
a[j]=a[j-1];
}
a[j]=x;//插入
}
}
}
希尔排序
先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。
public class ShellSort {
public static void main(String[] args) {
int a[] = {3,1,5,7,2,4,9,6,10,8};
ShellSort obj=new ShellSort();
System.out.println("初始值:");
obj.print(a);
obj.shellSort(a);
System.out.println("\n排序后:");
obj.print(a);
}
private void shellSort(int[] a) {
int dk = a.length/2;
while( dk >= 1 ){
ShellInsertSort(a, dk);
dk = dk/2;
}
}
private void ShellInsertSort(int[] a, int dk) {//类似插入排序,只是插入排序增量是1,这里增量是dk,把1换成dk就可以了
for(int i=dk;i<a.length;i++){
if(a[i]<a[i-dk]){
int j;
int x=a[i];//x为待插入元素
a[i]=a[i-dk];
for(j=i-dk; j>=0 && x<a[j];j=j-dk){//通过循环,逐个后移一位找到要插入的位置。
a[j+dk]=a[j];
}
a[j+dk]=x;//插入
}
}
}
public void print(int a[]){
for(int i=0;i<a.length;i++){
System.out.print(a[i]+" ");
}
}
}
简单选择排序(看一下)
在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。
public class SimpleSelectSort {
public static void main(String[] args) {
int a[] = {3,1,5,7,2,4,9,6,10,8};
SimpleSelectSort obj=new SimpleSelectSort();
System.out.println("初始值:");
obj.print(a);
obj.selectSort(a);
System.out.println("\n排序后:");
obj.print(a);
}
private void selectSort(int[] a) {
for(int i=0;i<a.length;i++){
int k=i;//k存放最小值下标。每次循环最小值下标+1
for(int j=i+1;j<a.length;j++){//找到最小值下标
if(a[k]>a[j])
k=j;
}
swap(a,k,i);//把最小值放到它该放的位置上
}
}
public void print(int a[]){
for(int i=0;i<a.length;i++){
System.out.print(a[i]+" ");
}
}
public void swap(int[] data, int i, int j) {
if (i == j) {
return;
}
data[i] = data[i] + data[j];
data[j] = data[i] - data[j];
data[i] = data[i] - data[j];
}
}
堆排序
在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。
public class SimpleSelectSort {
public static void main(String[] args) {
int a[] = {3,1,5,7,2,4,9,6,10,8};
SimpleSelectSort obj=new SimpleSelectSort();
System.out.println("初始值:");
obj.print(a);
obj.selectSort(a);
System.out.println("\n排序后:");
obj.print(a);
}
private void selectSort(int[] a) {
for(int i=0;i<a.length;i++){
int k=i;//k存放最小值下标。每次循环最小值下标+1
for(int j=i+1;j<a.length;j++){//找到最小值下标
if(a[k]>a[j])
k=j;
}
swap(a,k,i);//把最小值放到它该放的位置上
}
}
public void print(int a[]){
for(int i=0;i<a.length;i++){
System.out.print(a[i]+" ");
}
}
public void swap(int[] data, int i, int j) {
if (i == j) {
return;
}
data[i] = data[i] + data[j];
data[j] = data[i] - data[j];
data[i] = data[i] - data[j];
}
}
冒泡排序(看一下)
在要排序的一组数中,对当前还未排好序的范围内的全部数,对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒。
public class BubbleSort {
public static void main(String[] args) {
int a[] = {3, 1, 5, 7, 2, 4, 9, 6, 10, 8};
BubbleSort obj = new BubbleSort();
System.out.println("初始值:");
obj.print(a);
obj.bubbleSort(a);
System.out.println("\n排序后:");
obj.print(a);
}
private void bubbleSort(int[] a){
int j,k;
int flag=a.length;
while (flag > 0){//排序未结束标志
k = flag; //k 来记录遍历的尾边界
flag = 0;
for(j=1; j<k; j++) {
if (a[j - 1] > a[j]) {//前面的数字大于后面的数字就交换
//交换a[j-1]和a[j]
int temp;
temp = a[j - 1];
a[j - 1] = a[j];
a[j] = temp;//表示交换过数据
flag = j;//记录最新的尾边界
}
}
}
}
public void print(int a[]){
for(int i=0;i<a.length;i++){
System.out.print(a[i]+" ");
}
}
}
快速排序(看一下)
1)选择一个基准元素,通常选择第一个元素或者最后一个元素,
2)通过一趟排序讲待排序的记录分割成独立的两部分,其中一部分记录的元素值均比基准元素值小。另一部分记录的 元素值比基准值大。
3)此时基准元素在其排好序后的正确位置
4)然后分别对这两部分记录用同样的方法继续进行排序,直到整个序列有序。
public class QuickSort {
public static void main(String[] args) {
int a[] = {3,1,5,7,2,4,9,6,10,8};
QuickSort obj=new QuickSort();
System.out.println("初始值:");
obj.print(a);
int h=a.length-1;
obj.quickSort(a,0,h);
System.out.println("\n排序后:");
obj.print(a);
}
private void quickSort(int[] a,int low, int high) {
if(low<high){ //如果不加这个判断递归会无法退出导致堆栈溢出异常
int middle=getMiddle(a,low,high);
quickSort(a, 0, middle-1); //递归对低子表递归排序
quickSort(a, middle + 1, high); //递归对高子表递归排序
}
}
public int getMiddle(int[] a, int low, int high){
int key = a[low];//基准元素,排序中会空出来一个位置
while(low<high){
while(low<high && a[high]>=key){//从high开始找比基准小的,与low换位置
high--;
}
a[low]=a[high];
while(low<high && a[low]<=key){//从low开始找比基准大,放到之前high空出来的位置上
low++;
}
a[high]=a[low];
}
a[low]=key;//此时low=high 是基准元素的位置,也是空出来的那个位置
return low;
}
public void print(int a[]){
for(int i=0;i<a.length;i++){
System.out.print(a[i]+" ");
}
}
}
归并排序
输入数据逆序情况下排序速度最快
归并排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
public class MergeSortTest {
public static void main(String[] args) {
int[] data = new int[] { 5, 3, 6, 2, 1, 9, 4, 8, 7 };
print(data);
mergeSort(data);
System.out.println("排序后的数组:");
print(data);
}
public static void mergeSort(int[] data) {
sort(data, 0, data.length - 1);
}
public static void sort(int[] data, int left, int right) {
if (left >= right)
return;
// 找出中间索引
int center = (left + right) / 2;
// 对左边数组进行递归
sort(data, left, center);
// 对右边数组进行递归
sort(data, center + 1, right);
// 合并
merge(data, left, center, right);
print(data);
}
/**
* 将两个数组进行归并,归并前面2个数组已有序,归并后依然有序
*
* @param data
* 数组对象
* @param left
* 左数组的第一个元素的索引
* @param center
* 左数组的最后一个元素的索引,center+1是右数组第一个元素的索引
* @param right
* 右数组最后一个元素的索引
*/
public static void merge(int[] data, int left, int center, int right) {
// 临时数组
int[] tmpArr = new int[data.length];
// 右数组第一个元素索引
int mid = center + 1;
// third 记录临时数组的索引
int third = left;
// 缓存左数组第一个元素的索引
int tmp = left;
while (left <= center && mid <= right) {
// 从两个数组中取出最小的放入临时数组
if (data[left] <= data[mid]) {
tmpArr[third++] = data[left++];
} else {
tmpArr[third++] = data[mid++];
}
}
// 剩余部分依次放入临时数组(实际上两个while只会执行其中一个)
while (mid <= right) {
tmpArr[third++] = data[mid++];
}
while (left <= center) {
tmpArr[third++] = data[left++];
}
// 将临时数组中的内容拷贝回原数组中
// (原left-right范围的内容被复制回原数组)
while (tmp <= right) {
data[tmp] = tmpArr[tmp++];
}
}
public static void print(int[] data) {
for (int i = 0; i < data.length; i++) {
System.out.print(data[i] + "\t");
}
System.out.println();
}
}
基数排序
把数据分组,放在一个个的桶中,然后对每个桶里面的在进行排序。 基数排序就是进行多次桶排序。
3. 面向对象
3.1什么是类、对象、类和对象的关系?
类:抽象的图纸,用于制造对象 对象:被制造出来的有属性的实体
3.2如何理解面向对象?
要想调用类种的属性和方法必先创建该类的对象。
3.3什么是属性、什么是方法?
属性就是对象的特征,方法就是对对象进行的操作
3.4如何创建对象、创建对象的时候JVM都做了什么事情 ?
1.new语句创建对象
2.使用反射,调用java.lang.Class或者java.lang.reflect.Constructor的newInstance()实例方法
3.调用对象的clone()方法
4.运用反序列化,调用java.io.ObjectInputStream()方法
new一个对象的时候JVM都做了那些事情
——之前没有进行类加载
1.类加载,同时初始化类中静态的属性(赋默认值,随即赋声明的值)
2.执行静态代码块
3.为类分配内存空间,初始化非静态的属性 (赋默认值)
4.调用父类构造器
5.父类构造器执行完后,如果自己声明属性的同时有显示的赋值,那么进行显示赋值把默认值覆盖
6.执行匿名代码块
7.执行构造器
8.返回内存地址
——之前已经进行了类加载
1.分配内存空间,同时初始化非静态的属性(赋默认值)
2.调用父类构造器
3.父类构造器执行完后,如果自己声明属性的同时有显示的赋值,那么进行显示赋值把默认值覆盖
4.执行匿名代码块
5.执行构造器
6.返回内存地址
3.5构造函数、构造函数重载
(1). 一般函数是用于定义对象应该具备的功能。而构造函数定义的是,对象在调用功能之前,在建立时,应该具备的一些内容。也就是对象的初始化内容。
(2). 构造函数是在对象建立时由jvm调用, 给对象初始化。一般函数是对象建立后,当对象调用该功能时才会执行。
(3). 普通函数可以使用对象多次调用,构造函数就在创建对象时调用。
(4). 构造函数的函数名要与类名一样,而普通的函数只要符合标识符的命名规则即可。
(5). 构造函数没有返回值类型。
public class Person{
String name;
int age;
//构造方法
Person(String n, int a){
name = n;
age = a;
}
public static void main(String[] args){
Person p = new Person("张三",1);//调用构造方法
System.out.println(p.name+p.age);
}
}
3.6参数的传递
基本数据类型传递 调用后数值不会发生变化
引用数据类型传递 调用后地址不会发生变化,但数值可能会发生变化
3.7方法的声明
访问修饰符 返回值类型 方法名 (形参){ 方法体 }
3.8方法的调用、属性的调用
通过类创建一个对象,由对象来调用
对象名.方法名() 对象名. 属性名
3.9方法重载
类名相同但参数和作用不同
3.10封装
封装的目的在于保护数据,将设计者和使用者分开,通过private关键字来实现。应用(javabean)、在java中没有特殊情况变量都是私有的。
3.11继承的概念、特征、目的
子类继承父类的所有方法和属性(不能调用私有的)
java支持单继承(一个子类只能有一个父类),私有属性无法直接调用
目的:提高代码复用性
3.12方法重写、什么样的方法不能被重写。
子类重写父类的方法 final定义的无法重写
重写和重载的区别:
Java中的方法重载发生在同一个类里面两个或者是多个方法的方法名相同但是参数不同的情况。
方法重写是说子类重新定义了父类的方法。方法覆盖必须有相同的方法名,参数列表和返回类型。覆盖者可能不会限制它所覆盖的方法的访问。
3.13抽象类的概念、和普通类的区别、特性
没有方法体 用abstract修饰 子类必须覆盖所有抽象方法后才能实例化用不能创建对象来派生子类,
只能派生子类,不能创建对象
内部可以没有抽象方法
不能与final同时使用
3.14抽象方法
概念:只是声明了一个类,而没有方法体
目的:重写
3.15接口的概念、的意义、特征?
interface定义
interface usb{
public abstract void print();
public static final int a = 10;
}
interface call extends usb{
public void callyou();
}
内部方法都是抽象的
定义的全局常量必须public static final 方法必须是public abstract开头
interface使用
//先实现接口
static class computer implements call{
public void print() {
System.out.println("i am a computer");
}
public void callyou() {
System.out.println("i will call");
}
}
public class Baidujingyan {
public static void main(String[] args) {
computer computeri=new computer();
computeri.print();
computeri.callyou();
}
}
输出
i am a computer
i will call
3.16接口和抽象类的异同点?
抽象类是代码重构的结果而接口是设计的结果。
参数 | 抽象类 | 接口 |
---|---|---|
默认的方法实现 | 它可以有默认的方法实现 | 接口完全是抽象的。它根本不存在方法的实现 |
实现 | 子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现。 | 子类使用关键字implements来实现接口。它需要提供接口中所有声明的方法的实现 |
构造器 | 抽象类可以有构造器 | 接口不能有构造器 |
与正常Java类的区别 | 除了你不能实例化抽象类之外,它和普通Java类没有任何区别 | 接口是完全不同的类型 |
访问修饰符 | 抽象方法可以有public、protected和default这些修饰符 | 接口方法默认修饰符是public。你不可以使用其它修饰符。 |
main方法 | 抽象方法可以有main方法并且我们可以运行它 | 接口没有main方法,因此我们不能运行它。 |
多继承 | 抽象方法可以继承一个类和实现多个接口 | 接口只可以继承一个或多个其它接口 |
速度 | 它比接口速度要快 | 接口是稍微有点慢的,因为它需要时间去寻找在类中实现的方法。 |
添加新方法 | 如果你往抽象类中添加新的方法,你可以给它提供默认的实现。因此你不需要改变你现在的代码。 | 如果你往接口中添加方法,那么你必须改变实现该接口的类。 |
3.17多态
里氏代换原则:只要父类能出现的地方子类就可以出现,而且替换为子类还不产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。但是,反过来就不行了,有子类出现的地方,父类未必就能适应
3.18内部类
普通内部类 定义在类中的类
局部内部类 只能在该类中使用的类
静态内部类 定义在类中且用static修饰的类
匿名内部类 只使用一次或不想命名
是另外一中多态的体现。
3.19工厂模式
简单工厂模式
工厂是一个简单的类,不是抽象类或者是接口,其中生成返回具体产品通常使用if-else或者swith-case。
返回产品的方法一般都是static,所有也称之为静态工厂方法模式(Static FactoryMethod Pattern)。
也可以认为:简单工厂模式看为工厂方法模式的一种特例,所以优缺点等介绍将不做,具体请看工厂方法模式。
工厂方法模式
生成返回具体产品是通过实现抽象工厂类或者工厂接口方式。
一组产品依赖实现一个抽象类或接口。
一个工厂工具(工厂实现类)返回产品组中一个具体产品类。
抽象工厂模式
提供创建一组相关相互依赖对象的接口,而无需指定他们具体的类。
工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则是针对的多个产品等级结构。
抽象工厂实现类提供的产品可以为多个,这多个产品可以实现依赖于不同的产品接口或抽象类产品。
4.访问控制符
1.常用控制符
2.static
随着类的加载而加载
优先于对象存在
被类的所有对象共享
可以通过类名调用
3.final
final修饰类:该类不能被继承
final修饰方法:该方法不能被重写
final修饰变量:该变量不能被重新赋值
和finally的区别:finally只能用在try catch语句中表示该语句必须被执行
4.this
代表当前类的对象的引用,即代表当前类的一个对象
5.super
代表父类的引用
6.instance of
instanceof运算符的前一个操作符是一个引用变量,后一个操作数通常是一个类(可以是接口),用于判断前面的对象是否是后面的类,或者其子类、实现类的实例。如果是返回true,否则返回false
5. 异常处理
1.什么是异常
异常是发生在程序执行过程中阻碍程序正常执行的错误事件.
2.异常的分类
Error JAVA环境编译错误
Runtime Exception 运行处异常
Exception 标准Java库方法异常
throw 用户自定义异常
3.异常的处理方法:2种
使用 try 和 catch 关键字可以捕获异常。try/catch 代码块放在异常可能发生的地方。
如果一个方法没有捕获到一个检查性异常,那么该方法必须使用 throws 关键字来声明。throws 关键字放在方法签名的尾部。也可以使用 throw 关键字抛出一个异常,无论它是新实例化的还是刚捕获到的。
4.Throw和throws的区别
throw语句用在方法体内,表示抛出异常,由方法体内的语句处理。
throws语句用在方法声明后面,表示抛出异常,由该方法的调用者来处理。
throws主要是声明这个方法会抛出这种类型的异常,使它的调用者知道要捕获这个异常。
throw是当程序出现某种逻辑错误时由程序员主动抛出某种特定类型的异常是,具体向外抛异常的动作,所以它是抛出一个异常实例。
throw与throws的比较
throws出现在方法函数头;而throw出现在函数体。
throws表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某种异常对象。
两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。
5.自定义异常(写一下)
6.常用系统类
1.String(常用方法20个)、StringBuffer、StringBuilder
String (对象)
String str=new String("a"); //创建a字符串
str.length(); //获取字符串长度
str.charAt(int i);//截取第i个字符
str.getChars() //截取多个字符
str.getBytes()//将字符存储在字节数组中
str.equals()//比较两个字符串
str.equalsIgnoreCase() //比较两个字符串,忽略大小写
str.regionMatches() //用于比较一个字符串中特定区域与另一特定区域
ste.startsWith()//以特定字符串开始
str.endsWith()//以特定字符串结束
str.indexOf(String p); //p第一次出现的位置,没有则返回-1
str.indexOf(String p,inti); //p在第i个字符后第一次出现的位置,没有则返回-1
str.concat(String t); //将t附在字符串的末尾,也可用+号来连接
str.substring(int i,int j); //从i到j的子字符串
str.split(String a); //把字符串以a字符分开,返回一个字符串数组
str.hashCode(); //哈希码
str.trim() //去掉起始和结尾的空格
str.replace(old,new)//替换某字符
str.valueOf() //转换为字符串
str.toLowerCase()//转换为小写
str.toUpperCase()//转换为大写
Stringbuffer
StringBuffer strbf=new StringBuffer("exception"); //创建一个StringBuffer类的字符串
strbf.append(String s);//将指定的字符串追加到此字符序列。
strbf.reverse(); //将此字符序列用其反转形式取代。
strbf.delete(int start, int end); //移除此序列的子字符串中的字符。
strbf.insert(int offset, int i); //将 int 参数的字符串表示形式插入此序列中。
strbf.replace(int start, int end, String str); //使用给定 String 中的字符替换此序列的子字符串中的字符。
2.new String(“A”)+new String(“B”) +”AB”创建了几个对象
“A”–(1),new String(A)–(2),“B”–(3),new String(B)–(4)“AB”–(5),AB+AB–(6)
注意:String类不可被继承(被final修饰)
3.Math
Math.E //自然常数e=2.7182818284590452354
Math.abs(12.3); //12.3 返回该值的绝对值
Math.abs(-12.3); //12.3
Math.copySign(1.23, -12.3); //-1.23,返回第一个参数的量值和第二个参数的符号
Math.copySign(-12.3, 1.23); //12.3
Math.signum(x); //如果x大于0则返回1.0,小于0则返回-1.0,等于0则返回0
Math.signum(12.3); //1.0
Math.signum(-12.3); //-1.0
Math.signum(0); //0.0
//指数
Math.exp(x); //e的x次幂
Math.expm1(x); //e的x次幂 - 1
Math.scalb(x, y); //x*(2的y次幂)
Math.scalb(12.3, 3); //12.3*2³
//取整
Math.ceil(12.3); //返回最近的且大于该值的整数13.0
Math.ceil(-12.3); //-12.0
Math.floor(12.3); //返回最近的且小于该值的整数12.0
Math.floor(-12.3); //-13.0
//x和y平方和的二次方根
Math.hypot(x, y); //√(x²+y²)
//返回该值的二次方根
Math.sqrt(x); //√(x) x的二次方根
Math.sqrt(9); //3.0
Math.sqrt(16); //4.0
//返回该值的立方根
Math.cbrt(27.0); //3
Math.cbrt(-125.0); //-5
//对数函数
Math.log(e); //1 以e为底的对数
Math.log10(100); //10 以10为底的对数
Math.log1p(x); //Ln(x+ 1)
//返回较大值和较小值
Math.max(x, y); //返回x、y中较大的那个数
Math.min(x, y); //返回x、y中较小的那个数
//返回x的y次幂
Math.pow(x, y);
Math.pow(2, 3); //即2³ 即返回:8
//随机返回[0,1)之间的无符号double值
Math.random();
//返回最接近该值的整数,如果居中,则取偶数
Math.rint(12.3); //12.0
Math.rint(-12.3); //-12.0
Math.rint(78.9); //79.0
Math.rint(-78.9); //-79.0
Math.rint(34.5); //34.0
Math.rint(35.5); //36.0
Math.round(12.3); //与rint相似,返回值为long
//三角函数
Math.sin(α); //sin(α)的值
Math.cos(α); //cos(α)的值
Math.tan(α); //tan(α)的值
//求角
Math.asin(x/z); //返回角度值[-π/2,π/2] arc sin(x/z)
Math.acos(y/z); //返回角度值[0~π] arc cos(y/z)
Math.atan(y/x); //返回角度值[-π/2,π/2]
Math.atan2(y-y0, x-x0); //同上,返回经过点(x,y)与原点的的直线和经过点(x0,y0)与原点的直线之间所成的夹角
Math.sinh(x); //双曲正弦函数sinh(x)=(exp(x) - exp(-x)) / 2.0;
Math.cosh(x); //双曲余弦函数cosh(x)=(exp(x) + exp(-x)) / 2.0;
Math.tanh(x); //tanh(x) = sinh(x) / cosh(x);
//角度弧度互换
Math.toDegrees(angrad); //角度转换成弧度,返回:angrad * 180d / PI
Math.toRadians(angdeg); //弧度转换成角度,返回:angdeg / 180d * PI
4.Random
5.Date、Cendler
使用字符串初始化Date
String dateString = “2018-02-23”;
Date date= new SimpleDateFormat(“yyyy-MM-dd”).parse(dateString);
使用时间戳初始化
Date date = new Date(时间戳);
计算两个日期相差天数
long day = date1.getTime() - date2.getTime()) / (246060*1000) + 1;
使用Calendar
Calendar calendar=Calendar.getInstance();
Date date=calendar.getTime);
获取年月日
calendar.get(Calendar.YEAR);
alendar.get(Calendar.MONTH) + 1;
calendar.get(Calendar.DAY_OF_MONTH);
calendar.get()方法
calendar.get(Calendar.DAY_OF_WEEK); 当前周的第几天
calendar.get(Calendar.DAY_OF_MONTH); 当前月的第几天
calendar.get(Calendar.DAY_OF_YEAR); 当前年的第几天
6.Arrays
给数组赋值:通过fill方法。
对数组排序:通过sort方法,按升序
比 较 数 组: 通过equals方法比较数组中元素值是否相等。
查找数组元素:通过binarySearch方法能对排序好的数组进行二分查找法操作。
截取数组元素:通过copyOf方法和copyOfRange对数组元素进行截取。
打印数组元素:通过toString方法打印数组元素。
将数组转换为list:通过asList方法转换为List。
7.System
System类提供了代表标准输入、标准输出和错误输出的类变量,并提供一些静态方法用于访问环境变量、系统属性的方法,还提供了加载文件和动态链接库的方法
8.基本数据类型和包装类、自动装箱和自动拆箱
基本数据类型:初值为0
包装类引用指向一个对象,初值为null
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean
Integer i2 = 100; //自动装箱,把基本数据类型转换成对象(jdk5以上)
int z = i2 + 200; //自动拆箱,把对象转换为基本数据类型
7.集合
1.Javabean的特性、作用
(1)所有的类必须放在一个包中,在WEB中没有包的是不存在的
(2)所有的类必须声明为public class,这样才能够被外部所访问;
(3)类中所有的属性都必须封装,即:使用private声明;
(4)封装的属性如果需要被外部所操作,则必须编写对应的setter、getter方法;
(5)一个JavaBean中至少存在一个无参构造方法,此为JSP中的标签所使用。
//Javabean示例
public class User {
private String username = null;
private String password = null;
//get set方法
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
//构建无参构造函数
public User() {
super();
// TODO Auto-generated constructor stub
}
}
2.List\Set\Map的区别
Set接口:
无序可变的数组,不允许添加重复元素,如果视图把两个相同的元素加入到同一个集合中,add方法返回false.
set判断对象是否相同使用equals方法, 就是说返回true就表示两个对象相同 set不会接受.
List接口
一个 List 是一个元素有序的、可以重复、可以为 null 的集合(有时候我们也叫它“序列”)
Map接口
Map是一种把键对象和值对象映射的集合,它的每一个元素都包含一对键对象和值对象。 Map没有继承于Collection接口 从Map集合中检索元素时,只要给出键对象,就会返回对应的值对象
3.List的常用子类、区别
ArrayList
代表长度可以改变得数组。可以对元素进行随机的访问,向ArrayList()中插入与删除元素的速度慢。
LinkedList:在实现中采用链表数据结构。插入和删除速度快,访问速度慢。
Vector
Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢。
4.Set的常用子类、区别
HashSet
不能保证元素的排列顺序,
不是同步的(线程不安全)
集合元素可以是null,但只能放入一个null
当向HashSet集合中存入一个元素时,HashSet会调用该对象的HashCode()方法来得到该对象的HashCode值,然后根据HashCode值来决定该对象在HashSet中的储存位置.
判断两个HashSet元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的HashCode()方法返回值相等
如果要一个对象放入HashSet中,重写该对象对应类的equals方法,也应该重写HashCode()方法.
LinkedHashSet
LinkedHashSet集合同样是根据HashCode()方法来决定元素的存储位置,但是它同时使用链表维护元素的次序.这样使得元素看起来像是以插入顺序保存的,当遍历该集合的时候,LinkedHashSet将会以元素的添加顺序访问集合的元素.
LinkedHashSet在迭代访问Set中的全部元素时,性能比HashSet好,但是插入时性能稍逊色于HashSet.
TreeSet
TreeSet是SortedSet接口的唯一实现类,TreeSet可以确保集合元素处于排序状态。
TreeSet支持两种排序方式,自然排序 和定制排序,其中自然排序为默认的排序方式。
TreeSet判断两个对象不相等的方式是两个对象通过equals方法返回false,或者通过CompareTo方法比较没有返回0
5.Map的常用子类、区别
HashTable
类实现一个哈希表,该哈希表将键映射到相应的值。任何非 null 对象都可以用作键或值。为了成功地在哈希表中存储和获取对象,用作键的对象必须实现 hashCode 方法和 equals 方法。
HashMap
Map基于散列表的实现。插入和查询“键值对”的开销是固定的。可以通过构造器设置容量capacity和负载因子load factor,以调整容器的性能。
LinkedHashMap
类似于HashMap,但是迭代遍历它时,取得“键值对”的顺序是其插入次序,或者是最近最少使用(LRU)的次序。只比HashMap慢一点。而在迭代访问时发而更快,因为它使用链表维护内部次序。
TreeMap
基于红黑树数据结构的实现。查看“键”或“键值对”时,它们会被排序(次序由Comparabel或Comparator决定)。TreeMap的特点在 于,你得到的结果是经过排序的。TreeMap是唯一的带有subMap()方法的Map,它可以返回一个子树。
6.Collection和Conllections的区别、Conllections的常用方法
Collection是集合类的上级接口,继承与他有关的接口主要有List和Set
Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全等操作
Collections的常用方法
static void swap(List list,int i,int j) //将指定列表中的两个索引进行位置互换
static void shuffle(List list) //随机置换
static void fill(List list,Object obj) //使用指定对象填充指定列表的所有元素
static void copy(List dest,List src) //把源列表中的数据覆盖到目标列表
static int binarySearch(List list,Object key) //使用二分查找法查找指定元素在指定列表的索引位置
7.TreeSet的2种对象的排序(Comparable、Compartor)
TreeSet集合默认会进行排序。因此必须有排序,如果没有就会报类型转换异常。
自然排序Comparable
public static void main(String[] args) {
demo d = new demo();
Custumer c1 = d.new Custumer(1, 1, "1");
Custumer c2 = d.new Custumer(4, 2, "2");
Custumer c3 = d.new Custumer(2, 3, "3");
Custumer c4 = d.new Custumer(2, 4, "4");
Custumer c5 = d.new Custumer(5, 5, "5");
List<Custumer> custumers = new ArrayList<demo.Custumer>();
custumers.add(c1);
custumers.add(c2);
custumers.add(c3);
custumers.add(c4);
custumers.add(c5);
for(Custumer custumer:custumers){
System.out.print(" "+custumer.getName());
}
System.out.println("\n----------------------");
Collections.sort(custumers);
for(Custumer custumer:custumers){
System.out.print(" "+custumer.getName());
}
}
class Custumer implements Comparable<Custumer> {
private int id;
private int age;
private String name;
public Custumer(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int compareTo(Custumer o) {
// TODO Auto-generated method stub
//根据id排序,如果相同根据age排序
if (o.getId() > id) {
return -1;
} else if (o.getId() < id) {
return 1;
} else {
if (o.getAge() > age) {
return -1;
} else if (o.getAge() < age) {
return 1;
} else {
return 0;
}
}
}
}
比较器排序Compartor
/**
* 具体的比较类(比较器),实现Comparator接口
* @author breeze
*
*/
public class ComparatorConsunInfo implements Comparator<ConsumInfo> {
/**
* 顺序(从小到大):
* if(price < o.price){
return -1;
}
if(price > o.price){
return 1;
}
* 倒序(从大到小):
* if(price < o.price){
return 1;
}
if(price > o.price){
return -1;
}
*/
@Override
public int compare(ConsumInfo o1, ConsumInfo o2) {
//首先比较price,如果price相同,则比较uid
if(o1.getPrice() > o2.getPrice()){
return 1;
}
if(o1.getPrice() < o2.getPrice()){
return -1;
}
if(o1.getPrice() == o2.getPrice()){
if(o1.getUid() > o2.getUid()){
return 1;
}
if(o1.getUid() < o2.getUid()){
return -1;
}
}
return 0;
}
}
/**
* 需要进行比较的类
* @author breeze
*
*/
public class ConsumInfo{
private int uid;
private String name;
private double price;
private Date datetime;
public ConsumInfo() {
// TODO Auto-generated constructor stub
}
public ConsumInfo(int uid,String name,double price,Date datetime){
this.uid = uid;
this.name = name;
this.price = price;
this.datetime = datetime;
}
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public Date getDatetime() {
return datetime;
}
public void setDatetime(Date datetime) {
this.datetime = datetime;
}
@Override
public String toString() {
return "ConsumInfo [uid=" + uid + ", name=" + name + ", price=" + price
+ ", datetime=" + datetime + "]";
}
}
8.堆、栈、队列、链表、二叉树
堆
堆(heap),是一类特殊的数据结构的统称。它通常被看作一棵树的数组对象。在队列中,调度程序反复提取队列中的第一个作业并运行,因为实际情况中某些时间较短的任务却可能需要等待很长时间才能开始执行,或者某些不短小、但很重要的作业,同样应当拥有优先权。而堆就是为了解决此类问题而设计的数据结构。
栈
栈是具有一定操作约束的线性表,只在一端(栈顶Top)做插入删除,后进先出
队列
队列是具有一定操作约束的线性表,只能在一端插入,在另一端删除
队列是先进先出(FIFO)的线性结构
链表
定义节点类
定义操作类
二叉树
二叉树是一个每个最结最多只能有两个分支的树,左边的分支称之为左子树,右边的分支称之为右子树。
8.流
1.流的概念、分类
概念
数据的流向,流动称之为流
分类
按方向分:
输入流(InputStream、Reader)
输出流(OutputStream、Writer)
按节点分:
字节流(InputStrem/outputStream)
字符流(Reader/Writer,不能处理音频、视频等)
按功能分:
处理流(以节点流为参数的流)
节点流
2.常用流的特性、API
9. 多线程
1.线程和进程的概念、区别
线程
在Java中,“线程”指两件不同的事情:
java.lang.Thread类的一个实例
线程的执行
进程
进程是拥有一个执行流,或多个执行流的线程组。
进程是一个能独立运行的基本单位,同时也是系统分配资源基本单位
2.如何创建多线程:(5个)
继承Thread类创建线程类
public class FirstThreadTest extends Thread{
int i = 0;
//重写run方法,run方法的方法体就是现场执行体
public void run()
{
for(;i<100;i++){
System.out.println(getName()+" "+i);
}
}
public static void main(String[] args)
{
for(int i = 0;i< 100;i++)
{
System.out.println(Thread.currentThread().getName()+" : "+i);
if(i==20)
{
new FirstThreadTest().run();
new FirstThreadTest().run();
}
}
}
}
通过Runable接口创建线程类
public class RunnableThreadTest implements Runnable
{
private int i;
public void run()
{
for(i = 0;i <100;i++)
{
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
public static void main(String[] args)
{
for(int i = 0;i < 100;i++)
{
System.out.println(Thread.currentThread().getName()+" "+i);
if(i==20)
{
RunnableThreadTest rtt = new RunnableThreadTest();
new Thread(rtt,"新线程1").start();
new Thread(rtt,"新线程2").start();
}
}
}
}
通过Callable和FutureTask创建线程
public class CallableThreadTest implements Callable<Integer>
{
public static void main(String[] args)
{
CallableThreadTest ctt = new CallableThreadTest();
FutureTask<Integer> ft = new FutureTask<Integer>(ctt);
// Thread thread = new Thread(ft,"有返回值的线程");
// thread.start();
for(int i = 0;i < 100;i++)
{
System.out.println(Thread.currentThread().getName()+" 的循环变量i的值"+i);
if(i==20)
{
new Thread(ft,"有返回值的线程").start();
}
}
try
{
System.out.println("子线程的返回值:"+ft.get());
} catch (InterruptedException e)
{
e.printStackTrace();
} catch (ExecutionException e)
{
e.printStackTrace();
}
}
@Override
public Integer call() throws Exception
{
int i = 0;
for(;i<100;i++)
{
System.out.println(Thread.currentThread().getName()+" "+i);
}
return i;
}
}
通过线程池创建线程
public class ThreadPool
{
/* POOL_NUM */
private static int POOL_NUM = 10;
/**
* Main function
*/
public static void main(String[] args)
{
ExecutorService executorService = Executors.newFixedThreadPool(5);
for(int i = 0; i<POOL_NUM; i++)
{
RunnableThread thread = new RunnableThread();
executorService.execute(thread);
}
}
}
class RunnableThread implements Runnable
{
private int THREAD_NUM = 10;
public void run()
{
for(int i = 0; i<THREAD_NUM; i++)
{
System.out.println("线程" + Thread.currentThread() + " " + i);
}
}
}
3.线程的生命周期
4.线程的交互的方法
线程交互中用到的三个基本函数:
void notify();//唤醒在此对象监视器上等待的单个线程。
void notifyAll();//唤醒在此对象监视器上等待的所有线程。
void wait();//导致当前的线程等待,直到其他线程调用此对象的notify()或者notifyAll()方法。
void wait(long timeout);//wait()的重载版本,同样导致当前线程等待,直到其他线程调用此对象的notify()或者notifyAll()方法,或者等待超过指定的时间后不再等待。
void wait(long timeout,int nanos);//wait()的重载版本,同样导致当前线程等待,直到其他线程调用此对象的notify()或者notifyAll()方法,或者等待超过某个实际的时间后不再等待,或者其他某个线程中断当前线程.
在线程交互中需要注意的是:
wait()、notify()、notifyAll()方法必须从同步环境中调用,线程不能调用对象上等待或通知的方法,除非此线程拥有对象的锁。
线程通过执行对象上的wait()方法获得等待列表,此后线程不再执行任何其他指令,直到调用对象的notify()方法为止。当多个线程在同一个对象上等待的时候,则notify()将随机的唤醒其中的一个线程来使其运行。若没有线程等待,则不做任何处理。
要执行wait()方法,前提是当前线程拥有此对象的锁,当执行wait()方法之后,此线程即释放此对象的锁,以便于其他线程中调用notify()或者notifyAll()来唤醒此线程。然而调用notify()时,不意味着线程会释放对象的锁。如果线程依然在完成同步代码,那么线程在完成同步代码块时不会释放对象的锁。所以,notify()并不意味着锁变得可用。
注意的问题:
wait(),notify(),notifyAll()不属于Thread类,而属于Object类,也就是说每个对象都有着三个方法。因为每个对象都有自己的锁,锁是每个对象的基础,操作锁的方法也是每个对象的基础。
wait()导致的是当前的线程的等待,而不是调用对象所在的线程类等待,也就是哪个线程拥有此对象的锁才能调用wait()方法。
notify()唤醒当前对象监视器上等待的单个线程,如果在此对象上有多个线程在等待,则随机的唤醒期中一个线程,直到当前线程放弃此对象上的锁,才能继续执行被唤醒的线程。notify()也和wait()一样,必须由持有此对象的锁的线程来调用,notifyAll()也一样,但是notifyAll()会唤醒在这个对象等待的所有线程,并从这个线程中随机的选择一个。
这三个方法都需要和synchronized配合使用,也就是必须先获取对象的锁,才能调用者三个方法。
5.线程的调度的方法
(1)定义
线程调度是指系统为线程分配处理器使用权的过程。主要调度方式有:协同式线程调度 和 抢占式线程调度
(2)协同式线程调度(Cooperative Threads-Scheduling)
定义
在此多线程系统中,线程的执行时间由线程本身控制,线程把自己的工作执行完之后,要主动通知系统切换到另外一个线程上。
优劣势
好处:实现简单,由于线程要把自己事情干完才会进行线程切换,切换操作对线程自己是可知的,所以没什么线程同步问题。
坏处:线程执行时间不可控,如果一个线程编写有问题,一直不告知系统进行线程切换,那么程序就会一直阻塞在那里。
(3)抢占式线程调度(Preemptive Threads-Scheduling)
定义
在此多线程系统中,每个线程将由系统来分配时间,线程的切换不由线程本身决定(Java中,Thread.yield() 可以让出执行时间,但是线程无法获取执行时间)。
优势
线程执行时间是系统可控的,也不会出现一个线程导致整个进程阻塞的问题。Java使用的线程调度方法就是这个。
(4)Java线程优先级
虽然Java 线程调度是系统自动完成的, 但我们还是可以建议系统给某些线程多分配一点执行时间,另外一些线程则可以少分配一点——这项操作可以通过设置线程优先级来完成。Java语言一共设置了10个级别的优先级,在两个线程同时处于 Ready状态,优先级越高的线程越容易被系统选择执行。
不过线程优先级并不是太靠谱,原因是因为Java的线程是通过映射到系统的原生线程上来实现的,所以线程调度最终还是取决于 操作系统,虽然现在很多os 都提供了线程优先级,但不见得和 能与 java线程的优先级一一对应。如 Solaris中有 2^32 种优先级,而windows只有7种 。
(5)Java 线程优先级与Windows线程优先级对应关系
(6)优先级可能会被系统自行改变
上文说到的“Java线程优先级并不是太靠谱”,不仅仅是在说一些平台上不同的优先级实际会变得相同这一点,还有其他情况让我们不能太依赖优先级:优先级可能会被系统自行改变。
例如:在Windows 中存在一个称为 “优先级推进器”的功能,作用是 当系统发现一个线程执行得特别勤奋的话,可能会越过线程优先级去为它分配执行时间。因此,我们不能在程序中通过优先级来完全准备判断一组状态都为Ready的线程将会先执行哪个
6.线程的同步(交替打印AB)与锁(对象锁)、死锁概念(写一下)
public class Thread {
static final Object object = new Object();
public static void main(String[] args){
//线程1
new java.lang.Thread(new Runnable() {
@Override
public void run() {
for(int i = 0;i<5;i++){
synchronized (object){
System.out.println("A");
object.notify(); //唤醒线程2
try {
object.wait();//线程1进入等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}).start();
//线程2
new java.lang.Thread(new Runnable() {
@Override
public void run() {
for(int i = 0;i<5;i++){
synchronized (object){
System.out.println("B");
object.notify();//唤醒线程1
try {
object.wait();//线程2进入等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}).start();
}
}
锁
在计算机科学中,锁(lock)或互斥(mutex)是一种同步机制,用于在有许多执行线程的环境中强制对资源的访问限制。锁旨在强制实施互斥排他、并发控制策略。
锁通常需要硬件支持才能有效实施。这种支持通常采取一个或多个原子指令的形式,如"test-and-set", “fetch-and-add” or “compare-and-swap””。这些指令允许单个进程测试锁是否空闲,如果空闲,则通过单个原子操作获取锁。
死锁
不允许两个进程同时占用。结果,两个进程都不能继续执行,若不采取其它措施,这种循环等待状况会无限期持续下去,就发生了进程死锁。
7.线程的资源共享(写一下)
1.如果每一个线程执行的代码相同,可以使用同一个runnable对象,这个对象中有那个共享数据(买票系统)
2.如果每一个线程执行的代码不相同,这时候需要不同的Runnable对象,有以下两种方式来实现这些Runnable对戏之间的数据共享。
(1)、将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个Runnable对象。每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。
(2)、将这些R0unnable对象作为某一个类中的内部类,共享数据作为这个外部类中的成员变量,每个线程对共享数据的操作方法也分配给外部类,以便实现对共享数据进行的各个操作的互斥和通信,作为内部类的各个Runnable对象调用外部类的这些方法。
(3)、上面两种方式的组合:将共享数据封装在另外一个对象中,每个线程对共享数据的操作方法也分配到那个对象身上去完成,对象作为这个外部类中的成员变量或方法中的局部变量,每个线程的Runnable对象作为外部类中的成员内部类或局部内部类。
(4)、总之,要同步互斥的几段代码最好是分别放在几个独立的方法中,这些方法再放在同一个类中,这样比较容易实现它们之间的同步互斥和通信。
3.极端且简单的方式,即在任意一个类中定义一个static的变量,这将被所有线程共享
在线程操作中由于其操作的不确定性,所以提供了一个方法,可以取得当前操作线程:
public static Thread currentThread();
8.线程组、守护线程(了解)
线程组(ThreadGroup)
在一个系统中,如果线程数量很多,而且功能分配比较明确,就可以将相同功能的线程放置在同一个线程组里。下面通过示例来说明。
守护线程(Daemon)
守护线程是一种特殊的线程,就像它的名字一样,它是系统的守护者。在后台默默的完成一些系统性的服务,比如垃圾回收线程、JIT线程就可以理解为守护线程。与之相应的是用户线程,用户线程可以认为是系统的工作线程,它会完成这个程序应该要完成的业务操作。如果用户线程全部结束,则意味着这个程序实际上无事可做了。守护线程要守护的对象已经不存在了,那么整个应用程序就应该结束。因此一个Java应用内只有守护线程时,Java虚拟机就会自然退出。
9.Lock锁(JDK的API)
10.线程池的原理、API
为什么使用线程池
在一些需要使用线程去处理任务的业务场景中,如果每一个任务都创建一个线程去处理,任务处理完过后,把这个线程销毁,这样会产生大量的线程创建,销毁的资源开销。使用线程池能有效的控制这种线程的创建和销毁,而且能够对创建的线程进行有效的管理。
11.生产者与消费者模式(原理)
生产者-消费者模式在系统交互方面,有几个特点:
1、系统解耦
2、解决并发问题
3、不需要关心对方系统何时处理数据,处理结果如何
生产者-消费者模式通过引入一个阻塞队列这个第三方组件来做到解耦和处理并发。
生产者只需要往队列里面塞数据,消费者只需要从队列中读取数据,生产者再也无需关注消费者处理数据的速度是如何了。生产者和消费者已经是完全独立的了。
利用某些队列特性,当生产者速度太快的话,数据超过了队列的最大阀值,那么可以自动阻塞住生产者(当然也可以设置线程阻塞的超时时间,防止消费者挂掉了,一直不处理队列中的数据,生产者),一直等到消费者先消费一些数据。
12.单例模式
定义
确保对象的唯一性,比如说买火车票,票源只有一个,购票方式可以有多个
分类
饿汉式
private static Ticket ticket = new Ticket();
public static Ticket getInstance() {
return ticket;
}
懒汉式
public synchronized static Ticket getInstance() {//synchronized 加锁
if(ticket == null) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket = new Ticket();
}
return ticket;
}
10. Socket
1.TCP/UDP
TCP: 是一个安全的协议,理解为打电话,能不能和对象通信需要对象给出应答
UDP: 广播协议,不安全只管发送,对方是否能够接受不关心,比如电视
2.ServerSocket和Socket的使用
1.使用serverSocket建立服务端:
首先创建一个 serverSocket
public class Service {
public static void main(String[] args) {
ServerSocket service = null;
OutputStream out = null;
BufferedWriter writer = null;
Socket socket = null;
InputStream in = null;
BufferedReader reader = null;
Scanner sc = new Scanner(System.in);
try {
service = new ServerSocket(9999);//创建了Socket服务对象
while (true){
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("service is init....");
//Socket对象是客户端和服务器端之间通信的对象,由Socket携带数据进行传输
socket = service.accept();//一旦程序运行后次方法将会阻塞,监听访问的程序,监听端口号 9999
System.out.println("service accept:" + socket.getPort());
in = socket.getInputStream();
reader = new BufferedReader(new InputStreamReader(in));
System.out.println(reader.readLine());
//谁可以帮助我将返回的数据传输出去=>Socket
String text = "陆嘉能" + df.format(new Date())+ ":"+sc.nextLine();
out = socket.getOutputStream();
writer = new BufferedWriter(new OutputStreamWriter(out));
writer.write(text);
writer.newLine();
writer.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//从里往关 安全
reader.close();
in.close();
writer.close();
out.close();
socket.close();
service.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
2、使用socket建立客户端
public class Client {
public static void main(String[] args) {
Socket socket = null;
BufferedWriter writer = null;
OutputStream out = null;
InputStream in = null;
BufferedReader reader = null;
Scanner sc = new Scanner(System.in);
try {
while (true){
String text = sc.nextLine();
socket = new Socket("localhost", 9999);//构建了一个socket对象用户访问和传输数据
out = socket.getOutputStream();
writer = new BufferedWriter(new OutputStreamWriter(out));
//向服务器写数据,写道Socket对象中
writer.write(text);
writer.newLine();
writer.flush();
/*-----------------------------------*/
//等待服务器响应,会返回数据
in = socket.getInputStream();
reader = new BufferedReader(new InputStreamReader(in));
System.out.println(reader.readLine());
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
writer.close();
out.close();
reader.close();
in.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
11.反射
1.反射的机制:
反射是一种技术,可以通过反射的技术创建对象,获取类中的属性和方法,并调用
2. 反射的常用API
2.1反射获取类
方法一
Class.forName("类的全路径 = 包路径 + 类名");
方法二
类名.class
方法三
对象名.getClass()
2.2 获取类的属性
Class c = Class.forName("类的全路径");
//获取类名
String name = c.getName();
2.3 获取类的一个对象
Object o = c.newInstance();
相当于
类名 对象名 = new 类名();
2.4 调用类的方法
Method md = c.getMethod("方法名",返回值类型.class);
md.invoke(o,方法所需参数);
12. XML解析
1. XML解析的四种方式
DOM SAX DOM4J JDOM
2.XML文件的规范
3.Dom4j的解析API
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.List;
/**
* @Author: cxx
* JDom解析xml
* 快速开发XML应用程序。
* 是一个开源项目
* JDOM主要用来弥补DOM和SAX在实际应用当中的不足。
*/
public class JDomDemo {
public static void main(String[] args) throws Exception {
//1.创建SAXBuilder对象
SAXBuilder saxBuilder = new SAXBuilder();
//2.创建输入流
InputStream is = new FileInputStream(new File("src/main/resources/demo.xml"));
//3.将输入流加载到build中
Document document = saxBuilder.build(is);
//4.获取根节点
Element rootElement = document.getRootElement();
//5.获取子节点
List<Element> children = rootElement.getChildren();
for (Element child : children) {
System.out.println("通过rollno获取属性值:"+child.getAttribute("rollno"));
List<Attribute> attributes = child.getAttributes();
//打印属性
for (Attribute attr : attributes) {
System.out.println(attr.getName()+":"+attr.getValue());
}
List<Element> childrenList = child.getChildren();
System.out.println("======获取子节点-start======");
for (Element o : childrenList) {
System.out.println("节点名:"+o.getName()+"---"+"节点值:"+o.getValue());
}
System.out.println("======获取子节点-end======");
}
}
}
13.JDBC(JDBC)
1.Connection、DriverMannager、Statement、ResultSet及API
DriverManger(驱动管理器)
DriverManger的作用有两个:
注册驱动:这可以让JDBC知道要使用的是哪个驱动;
获取Connection:如果可以获取到Connection,那么说明已经与数据库连接上了。
Connection
Connection对象表示连接,与数据库的通讯都是通过这个对象展开的:
Connection最为重要的一个方法就是用来获取Statement对象;
Statement
Statement是用来向数据库发送SQL语句的,这样数据库就会执行发送过来的SQL语句:
int executeUpdate(String sql):更新操作(insert、update、delete等),返回值是操作的计数;
ResultSet executeQuery(String sql):查询操作,数据库在执行查询后得到的结果集 ResultSet;
ResultSet
ResultSet对象表示查询结果集,只有在执行查询操作后才会有结果集的产生。结果集是一个二维的表格,有行有列。操作结果集要学习移动ResultSet内部的"行光标",以及获取当前行上的每一列上的数据,方法如下:
boolean next():使"行光标"移动到下一行,并返回移动后的行是否存在;
Object getXxx(int col):获取当前行指定列上的值,参数就是列数,列数从1开始,而不是0。
2.Java调用数据库的步骤(写一下)
导jar包:驱动!
加载驱动类:Class.forName(“类名”);
给出url、username、password,其中url背下来!
使用DriverManager类来得到Connection对象!
Connection对象创建Statement对象
Statement对象调用方法执行sql语句,得到结果。
3.Statement、PrepareStatment、CallableStatment的区别
Statement: 基本的
PreparedStatement:是可编译的,提高效率
callablestatment:存储过程.