int max=32;
int min=1;
Random random = new Random();
int s = random.nextInt(max)%(max-min+1) + min;
day1
数据类型:决定了变量占据多大的内存,以及变量存储什么样的数
float:精确到7位 double:精确到16位
java用的是Unicode编码
小数默认为double float小数赋值时要加f (如8.2f)
数据类型是有级别的: byt,short,char->int->long->float->double
运算时先统一类型再进行运算,以最高级别为准转换
低->高(自动转换)
转义字符:通过反斜杠改变后边所跟字符的含义
字符串和任何类型的数据相连接,最终都是字符串
取余符号% 结果的正负只与被除数有关
short s=5;
s=s+5;出错,高类型转低类型需要强转
s+=5;没错(内部进行了转换)
无符号右移,无论高位是什么都补0
其他移位运算:高位是什么就补什么
不用第三方变量进行互换
a=3,b=5;
a=a+b;
b=a-b;
a=a-b;
或者
a=a^b;
b=a^b;
a=a^b;
进制转换的位运算:
先与其最大的基数相与,再向右无符号位移
switch(a),a的类型:byte,short,int,char
JDK1.5后可以用枚举,JDK1.7后可以用String
day2:
continue;结束的是本次循环,直接进入下一次循环;(立刻执行i++忽略下面语句)
函数:具备特定功能的一般独立的代码段,什么时候用到该功能才去调用这个函数
当函数被调用时函数入栈,当全部语句执行完后出栈。
局部变量是在栈中开劈内存的,当局部变量的作用域结束了就会立刻释放内存
一个函数没有返回值也可以使用return
返回的含义:结束函数的执行
一个函数有返回值,那么在任何情况下都要有返回值
例如(a>b return a<b return)错! a=b的情况没有return
(在同一个class中,出现才是重载)
函数的重载:保证参数的个数不同,类型不同或个数类型不同和返回值类型不同*和返回值类型无关*
函数的重载:在同一个class中,出现了函数名称相同但参数不同的现象,保证参数的个数不同,类型不同或个数类型都不同
函数的重载和返回值类型无关
数组解决的问题,存储多个数据的问题
数组:可以存储多个数,属于引用数据类型,是一种数据类型
引用类型与基本类型的区别:引用类型需要用new关键词
int[] arr=new int[5];
类型 开辟连续的内存空间
arr存储的是数组在堆中的首地址,我们就称arr指向了数组,arr就是数据的引用
JVM把内存分为5块:栈,堆,方法区,本地方法区,寄存器
栈:
存储的是局部变量(在方法语句中定义的变量)
局部变量的作用域结束了会立刻释放内存
*进栈的没有默认值
堆:(特例字符串是存储在栈里的)
存储的用new创建的实体(对象,数组),都是可以存储多个数据的
用完立刻释放内存
特点:
*进堆的有默认值
int数组默认是0,字符串默认是null,Boolean数组默认flase
没有任何引用指向的数组就成了垃圾,不会立刻释放内存等待垃圾回收线程回收
数组常见异常:
数组下标越界:ArrayIndexOutOfBoundsException
空指针异常:NullPointerException
arr=null;
数组初始化的方式 int[] arr=new int[]{3,5,6,7,8,9};
int[] b={12,321,44,5,66,77,44,55};
排序:Demo16
二维数组:
day3
面向对象是基于面向过程的。
类属于引用数据类型
对象的属性用变量表示
对象的行为用函数表示
类是用来描述对象的
我们new的对象都是用编译好Car.class创建的
成员变量和局部变量的对比:
1:成员变量有默认值
局部变量没有默认值
2:成员变量是随着对象的创建在堆中开辟内存
局部变量是随着所在函数的被调用在栈中开辟内存
3:成员变量的作用范围是整个类
局部变量的作用范围是从其定义的位置开始到其所在的大括号结束
4:成员变量是当所属的对象被垃圾回收时才会释放内存
局部变量是当其作用域结束时立即释放内存
成员变量是随着对象的创建在堆中开辟内存的
封装:只对外界提供有用的属性和行为
构造函数:是用于创建对象的,一个类没有定义构造函数,那么系统会给一个默认的。如果自己在类中写了构造函数,那么默认的就不存在了
构造函数的特点:
函数名必须和类名相同
函数不能有返回值类型
只能用于创建对象,不能被单独调用
.class是放在方法区的,默认的构造方法也存在方法区里面
利用构造函数初始化成员变量
一个类可以有多个构造函数
this:是一个引用,总是指向当前对象
局部变量和成员变量同名时,成员变量失效
构造函数之间的调用:必须写在第一行
static关键字:可以修饰的是成员变量
被static修饰的成员变量的特点:
1:被static修饰的成员变量是随着所属类的加载的同时在方法区中的静态区开辟内存的
2;是其所属类的所有对象共享的
3:可以使用类名访问
缺点:占据内存时间太长
静态方法:静态只能使用静态
非静态方法:既可以使用静态也可以使用非静态
不用static修饰的变量称为实例变量
用static修饰的变量称为类变量
静态成员变量和非静态成员变量对比:
1:访问方式
非静态成员变量只能使用对象来访问
静态成员变量既可以通过对象访问也可以通过类访问
2:存储数据:
非静态成员变量存储的是每个对象特有的数据
静态成员变量存储的是其所属的类所有对象共享的数据
3:存储位置
非静态成员变量是随着对象的创建在堆中开辟内存
静态成员变量是随着所属类的加载同时在方法区中的静态区中开辟内存
4:生命周期
非静态成员变量是当对象创建时开辟内存,当对象被垃圾回收时释放内存
静态成员变量是随着所属类的加载同时在方法区中的静态区中开辟内存,程
序退出时才释放内存
什么情况下可以使用static修饰成员变量?
类的所有对象需要共享成员变量的值
什么情况下可以使用static修饰成员方法?
当该方法没有*直接*用到其所在类的任何非静态成员
因为静态方法只能使用静态的,所有如果方法用到了非静态成员,那么该方法不能是静态的
说明文件的生成javadoc d +路径 +.java文件
静态代码块:随着类的加载而执行,只执行一次,且优先于main函数的执行
static
{
System.out.println("c");
}
静态代码块可以用于执行当系统启动时就执行的初始化工作
构造代码块:对象一创建就执行,优先于构造函数的执行
{
System.out.println("c");
}
构造代码块可以把所有对象共性属性或行为写在其中
而构造函数用于对象特有属性的初始化
对象的初始化过程:
1:因为对象是依赖于字节码文件的,先加载字节码文件到方法区
2:如果有静态代码块会执行静态代码块
3:在堆中开辟内存
4:给成员变量赋默认值
5:如果有初始值给成员变量赋初始值
6:执行结构代码块
7:执行构造函数
8:把对象在堆中的内存首地址赋值给栈中的引用变量
java设计模式一共有23种
设计模式:解决某一类问题最有效的方式
单例设计模式:解决的是一个类在内存中只有一个对象的问题
1:构造方法私有化
2:构造方法私有化之后,一个对象都不能创建了,所有只能在类中创建对象
单例模式的两个方式
饿汉式
有没有用到都在内部new了一个
懒汉式
不NEW指向null;
后面接一个判断再创建
一般用饿汉式
继承:
1,实现了代码的重复使用2:使类和类之间产生了关系(父类,子类)
java中继承的特点:
单继承:一个类只能有一个父类
多继承:一个类可以有多个父类;多继承会出现调用的不确定性所有java不支持多继承
java支持多层继承
继承中成员变量的特点:
this:是一个引用,指向当前使用的对象
super:不是引用,代表的是父类的数据空间,作用是当子父类中出现同名现象时进行区分的
父类中成员是私有的,子类无权访问
继承中成员方法的特点:
当子类中出现了和父类中一样的方法时,创建子类对象并调用一样的方法时执行的是子类中的方法,这种现象就叫重写,也称作覆盖
重写的前提条件:存在继承关系
注意:
1:子类在重写时,子类中方法的权限必须大于等于父类中方法的权限
2:父类中private修饰的方法不能被重写
3:静态只能由静态覆盖
4:重写方法的返回值可以不一样,但子类的返回值一定是父类返回值的子类
5:父类中的方法没抛出异常,子类重写时绝对不能抛
6父类中的方法抛出异常,子类重写时可以抛出和父类相同的异常,也可以抛出子类异常
为何要重写这个语法:因为和父类的行为一样,只是表现方法不同
day04
继承中构造函数的特点:在创建子类对象时总是先执行父类的构造函数,再执行子类的构造函数
原因:是系统自动在子类的构造方法的第一行加入了一条语句,super()作用是调用父类的空参的构造方法
为什么先执行父类的构造函数?
因为子类继承父类,肯定会继承父类中的属性,先执行父类的构造方法,可以利用父类中给的属性赋值语句
注意:如果父类中没有空参的构造方法,那么子类必须手动加入super语句。
一般创建父类的时候应该手动写上空参的构造函数,保证子类不会出错
final:可以修饰类,方法,局部变量,成员变量
一个类被修饰为final的,那么这个类不能有子类
一个方法被修饰为final的,那么这个方法不能被重写
符号常量:public static final double PI=3.1415;
把引用修饰为final的,那么这个引用就不能指向别的对象了
抽象类:
也是类,是在对子类提取共性的过程中,出现了不能确定如何实现的方法,因为子类对方法有各自的实现方式,那么该类就不去实现这个方法了,这种没被实现的方法就是抽象方法,含有抽象方法的类就是抽象类
继承了抽象类,所以该类中也有抽象方法,如果没有实现该抽象方法,那么这个类也是抽象类
1;抽象类不能用于创建对象
2:一个类继承了抽象类,若没有实现抽象方法那么这个类中也含有抽象方法,也是抽象类
抽象类一定是父类?
抽象类一定是父类
抽象类和普通类的区别?
共同点:都是类
不同点:普通类可以创建对象,抽象类不可以
抽象类可能含有抽象方法,普通类不能含有抽象方法
抽象类有构造方法吗?
有构造方法,用于子类的初始化
abstract不能和哪些关键字同时使用?
1:final:抽象类必须能被继承,被final修饰的不能有子类
2:static:静态的方法可以通过类名访问,抽象方法不能被调用
3:private:私有的方法不能被重写,抽象方法必须可以被重写
抽象类一定含有抽象方法?
不一定,当只是希望类不能创建对象时,可以把类修饰为abstract的,但是类中没有抽象方法
接口:interface 接口名{}
成员变量默认修饰为 public static final
成员方法默认修饰为 public abstract
接口可以多实现,可以解决java的单继承问题
实现多个接口不会出现调用的不确定性,因为接口中的方法都是抽象的,都没有实现,实现接口的子类会重写该方法,创建子类对象并调用该方法时,很明确调用子类中重写的方法
接口定义时从JDK1.8开始可以定义已经实现的方法
public default void fun(){}
类和类之间是继承
类和接口之间是实现
通过继承可以得到继承体系中基本的属性和功能
通过实现接口可以得到体系之外的内容
接口和接口之间是继承关系而且可以多继承
接口:是对外暴露的一组规则
提高程序的扩展性
降低程度之间的耦合度
比较接口和抽象类
多态的前提:存在继承或实现
多态:父类类型的引用指向子类对象
多态的弊端:只能使用父类中定义的功能,子类特有的功能不能调用了
多态的好处:前期定义的功能可以被后来增加的对象使用,提高了程序的扩展性
在多态中使用子类特有的功能(类型转换回去就能用了)
Animal mao =new Cat();//向上转型
mao.eat();
//调用子类特有的功能
Cat cat=(Cat)mao;//强制向下转型
若在函数中使用:
public(Animal dongwu)
{
if(dongwu instanceof Cat)
{
Cat cat=(Cat)mao;
mao.catchMouse();
}
}
多态中成员的特点:
成员变量:编译时期看父类,运行结果看父类
成员方法:编译时期看父类,运行结果看子类(子类重写的方法)
静态方法:编译时期看父类,运行结果看父类
模板设计模式:实现一个功能时,功能的一部分是确定的,一部分是不确定的,确定的部分还会用到不确定的部分,那么就把不确定的部分暴露出去,让子类去实现
内部类:在一个类内部定义的类
成员内部类,因为不是静态的,所以需要创建外部类对象
静态内部类:如果内部类中的方法是静态的那么内部类必须是静态的
内部类访问外部类中成员(同名情况):外部类名.this.外部类成员名
Outer.this//得到外部类对象
匿名内部类:
interface Test
{
void func();
}
class Demo
{
public static void main(String[] args)
{
//补足代码;(匿名内部类)
new Demo().show(new Test(){
public void func()
{
}
});
}
void show(Test t)
{
t.func();
}
}
异常:程序在运行时出现的不正常情况
异常的分类:
Throwable:
Error:严重的错误,不该处理
Exception:可以处理的不正常情况
java把程序运行时出现的各种不正常情况提取共性的属性和行为,比如异常名称,异常信息,异常发生的位置从而出现了各种异常类
day5
try catch
如果try里面出现异常,那么会直接跳到catch里面。
默认,当一个函数可能发生异常时调用者可以处理液可以不处理,程序编译照样通过,但是会导致运行时异常,为了把运行时期的问题转移到编译时期所有使用throws声明抛出异常,那么调用者必须进行处理,处理方式有俩种:
1,使用try{}catch(){}处理
2,不处理异常必须用throws继续声明
catch子类异常要写在父类异常的上面
自定义异常:继承Exception/Throwable
在函数内部使用throw手动抛出异常对象,必须处理
处理方式有两种
1,使用try{}catch(){}
2,使用throws声明
处理方式由调用者来确定
正常的异常
throw和throws的对比:
throw后边跟的是异常类对象,throws后边更多是异常类
throw用在函数内部,throws用在函数名的后边
运行时异常(RunrimeException)或其子类-------我们常见的 异常,空指针,越界,数学异常
使用throw内部抛出异常类对象,不处理编译也通过
使用throws声明异常,不处理编译也通过
异常分为两类:
一类是编译时检测的异常:非运行时一异常
一类是编译时不检测的异常:一九四运行时异常
运行时异常不被检测,因为java希望当程序出现运行时异常时,程序就应该终端,不该处理完继续执行。
运行时异常都是由于数据传递错误造成的
finally里面放的是必须执行的语句,里面的语句是在return之前运行的
只有System.exit(1)程序结束才不执行
try{获取资源} finally{释放资源}结构跟异常没有关系,因为finally不能单独使用所以要加个try,主要用来释放资源
String:字符串是常量:它们的值在创建之后不能更改
字符串是存储在方法区中的常量池中的
String重写了Object中的equals方法,判断两个字符串的内容是否相同
StringBuffer:线程安全的,从jdk1.0开始
StringBuilder :线程不安全的,从jdk1.5开始出现,在不需要保证线程安全的情况下使用StringBuilder
jdk升级的原因
1:提高效率
2:提高安全性
3:简化书写
包装类的作用:
可以将基本类型转字符串类型
Ingeger.toString(int i);
字符串类型转基本类型
Integer.parseInt("123");
把十进制数转成其他进制:
Integer.toHexString(20);
Integer.toOctalString(20);
其他进制转成十进制
Integer.parseInt("3c",16);
Integer n=66;//自动装箱:内部实现了new Integer(66)
n=n+5//内部先拆箱再装箱
Integer a=127;
Integer b=127;
a==b;//true
Integer x=128;
Integer y=128;
x==y//false
在一个字节允许范围内,如果之前已经定义过了,那么再使用时使用已经定义过的
day6
date
Math
集合:可以存储不同类型的对象,随着存储对象个数的增长集合会自动增长
顶层是Cillection接口,下面是list和set子接口。list接口下面,ArrayList、LinkedList、vector类
set下面有HashSet、TreeSet类
iterator()返回的是内部类对象
因为对象在集合内部,所有内部类可以直接操作集合内部的对象
又因为集合的数据结构是不一样的,所以对内部类提取共性,形成接口Iterator
iterator方法返回的是内部类对象,再使用内部类对象的hasNext()和next()方法进行遍历
list:存储的数据是有序的(集合中存储的顺序和添加的顺序是一样的),可以重复的
ArrayList:底层使用的数据结构是数组,线程不安全的,查找速度快,增删速度慢
LinkedList:底层使用的数据结构是链表,线程不安全的,查找速度慢,增删速度快
Vector:底层使用的数据结构是数组,线程安全的,查找速度快,增删速度慢,被ArrayList取代了
set:存储的数据是无序的,不可以重复非
HashSet:底层使用的数据结构是哈希表,线程不安全的,替代了hashtable
如何保证添加的对象不重复? 用了hashcode(),equals()
先用对象的哈希值和集合中的每个对象的哈希值进行比较如果哈希值都不相同就直接把对象存入集合,如果出现哈希值相同的会再对两个对象使用equals()方法进行比 较,equals方法返回true则认为集合中已经存在该对象,不加入集合
哈希值相同不一定是同一个对象
同一个对象的哈希值是相同的
TreeSet:底层使用的数据结构是二叉树,线程安全
排序的第一种方式:集合中对象所属的类需要实现Comparable接口
TreeSet依据compareTo()方法的返回值判断谁大谁小
在添加的同时会排序使用的是compareTo()方法
TreeSet判断元素是否重复依据的是compareTo()方法的值,当等于零的时候就认为这两个元素一样
排序的第二种方式:当对象所属的类已经具备了比较大小的方法,但是该方法不是我们所需要的,那么需要单独定义比较大小方式,我们称为比较器
自定义的比较器需要实现Comparator接口中的int compare(Object a,Object b)
在迭代的过程中对集合进行添加,修改,删除操作会发生异常
listIterator可以解决这个问题
使用LinkedList实现栈,队列
/*
Collection:
List:存储的数据是有序的(集合中存储的顺序和添加的顺序是一样的),可以重复的
ArrayList:底层使用的数据结构是数组,线程不安全的,查找速度快,增删速度慢
LinkedList:底层使用的数据结构是链表,线程不安全的,查找速度慢,增删速度快
Vector:底层使用的数据结构是数组,线程安全的,查找速度快,增删速度慢,被ArrayList替代了
Set:存储的数据是无序的,不可以重复的
/*
Collection接口中定义的是最共性的方法:
添加:
boolean add(Object o)
boolean addAll(Collection<? extends E> c)
删除:
boolean remove(Object o)
boolean removeAll(Collection<?> c)
void clear()
判断:
boolean contains(Object o)
boolean containsAll(Collection<?> c)
boolean isEmpty() //判断集合是否为空
boolean equals(Object o)
获取:
Iterator<E> iterator() //获取集合中的对象
int size() //获取集合中对象的个数
集合变数组:
Object[] toArray()
List:特有方法,可以操作下标
增:
void add(int index, E element)
boolean addAll(int index, Collection<? extends E> c)
删
E remove(int index)
改
E set(int index, E element)
查
ListIterator<E> listIterator()
返回此列表元素的列表迭代器(按适当顺序)。
ListIterator<E> listIterator(int index)
List<E> subList(int fromIndex, int toIndex)
E get(int index)
*/
/*
LinkList独有的方法
addFirst()
addLast()
getFirst()//获取的对象不存在会异常
getLast()
removeFirst()//删除的对象不存在会异常
removeLast()
从jdk1.6开始出现以下方法
offerFirst()
offerLast()
peekFirst()//获取的对象不存在不会异常
peekLast()
pollFirst()//删除的对象不存在不会异常
polLast()
pop()
push()
*/
总结:
当用到contains()方法时:
ArrayList, 要进一步判断元素是否重复需重写boolean equals(Object obj)
HashSet, 要进一步判断是否有重复元素需重写public int hashCode()和public boolean equals(Object obj)
TreeSet, 要进一步判断元素是否重复需实现Comparable接口,重写public int compareTo(Object obj)方法
或者实现Comparator接口,重写public int compare(Object obj1,Object obj2);
day7
泛型:通过<引用数据类型>来接收一种引用数据类型,作用是在编译程序时就用该类型检查集合中存储的是否是该类型的对象,如果不是编译不通过,从而把运行时期的问题转移到编译时期,提高程序的安全性
使用泛型在迭代的时候就不用强制类型转换了
泛型擦除:泛型是在编译时期使用的,编译完之后的字节码文件中是没有泛型的
泛型方法:
当类上的泛型确定了方法的参数的类型才能确定
如果一个方法希望参数是任意类型,那么这个方法自己使用泛型
public <M> void fun(M m){}
静态方法进内存的时候还没有对象,所以静态只能自己用泛型
通配符:?
泛型限定:? extends E:可以是E类型或其子类类型,确定了上限
? super E:可以是E类型或其父类类型,确定了下限
public static void doeDai(Collection<? extenads Person> coll){}
Map:单独的接口,不属于collection体系,存储的是键值对,键不能重复,一对一对地存,可以存储null键,null值
HashMap:底层使用的是哈希表,是线程不安全的
TreeMap:底层使用的是二叉树,是线程不安全的
Set<k> keySet():得到所有键,存储到一个Set集合中,并返回该集合,因为Set有迭代器,所有得到Set集合的迭代器,每次迭代出来的是一个键,再根据键获取值
Set<Map.Entry<K,V>> entrySet():
得到每个键值对对应的映射关系类型的值,存储到一个Set集合中,并返回该集合,因为Set有迭代器,所有得到Set集合的迭代器,每次迭代出来的是一个映射关系类型的值,从该值中既可以得到键,也可以的得到值,映射关系类型就是Map.Entry<k,v>,Entry是定义在Map接口中的一个静态接口,Entry是对集合内部的键值对的映射关系的描述,所以定义在Map内部
存在映射关系才用map
Arrays.asList(b);
数组转成的集合,不可以添加或删除
数组转集合可以利用集合中的方法
int[] a={1,2,34} 类型转集合size()=1:集合存储的是引用类型
为什么集合转数组?因为数组的长度是固定的,所以当不希望数据被随意增删时把集合转数组
Collections集合工具类
增强循环:for(数据类型:变量,数组或者collection
可变参数:必须写在参数列表的最后
add(int... arr)
add(String a,int... arr)
import static java.util.Arrays.*;//静态导入
day8:
考试加做贪吃蛇
day9:
多线程:
进程:正在进行中的程序
也就是在内存中开辟的一块内存空间
线程:负责程序执行的一条执行路径
也称为一个执行单元
进程的执行实际上是线程的执行
一个进程至少包含一个线程
当一个进程中包含多个线程时,这个程序就是多线程程序
多线程的作用:在同一个程序中实现相同功能或不同功能同时执行的效果
多线程一定能提高效率吗?
不一定能提高效率,但是能合理的使用cpu资源,
最大的好处是实现在同一个程序中实现相同功能或不同功能同时执行的效果
JVM是多线程的吗?
至少有一个负责程序正常执行的线程,执行的是main函数中的代码---主线程
还有一个负责垃圾回收的线程,执行的是finalize函数中的代码---垃圾回收线程
任务:每个线程执行的代码
任务代码都有其存储位置
主线程的任务代码存储在main函数中
垃圾回收线程的任务代码存储在finalize函数中
线程是随着任务的存在而存在,随着任务的执行结束而消失
要同时显示需要使用多线程
创建线程的方式一:
1,创建一个类继承Thread(这个子类就是线程类)
2,重写Thread类的run方法
创建线程是为了执行任务
任务代码需要有存储位置,run方法就是存储位置
创建线程是为了执行任务,任务代码需要有存储位置
这个位置就是run方法,因为任务代码必须写在run方法中,
而Thread类中的run方法并没有实现。所以要用继承重写run方法
3,创建thread类的子类对象
4,启动
线程和线程任务是绑定在一起的,线程只能执行这个任务(写死了)
创建线程的方式二:
1,创建一个类实现Runnbale接口的类
2,该类实现run方法
3,创建该类的实例
4,创建Thread对象实际就是在创建线程
5,把实现了Runnable接口的类的实例传递给Thread对象的构造方法
特点
实现了线程任务和线程的分离,线程执行的什么任务不再重要了,只要实现了runnable接口的子类对象都可以作为参数传递给线程的构造方法
实现了Runnable接口的类是描述任务的类
实现接口的同时还可以继承父类
任何一个对象都可以被回收,所以回收的代码在Objec里面了,功能在Object中定义了finalize()方法
System.gc();//启动垃圾回收线程
多线程程序没词运行的结果是不确定的,这就是多线程程序运行的特点
守护线程:主线程结束守护线程也会同时结束
start方法会启动线程,并自动去执行run方法中的任务代码
run方法只是一个普通的方法调用,没有启动线程的功能
每个线程在栈中都有自己的一块内存(但与先进后出无关)
线程执行完自己的任务代码就结束了,就会释放在栈中占据的内存,只有当所有进程全部结束,整个进程才结束
线程栈中的异常:每个线程发生异常只是线程自己的事儿,不会影响其他线程
为什么创建线程的第二种方式能解决买票问题?
创建线程的第一种方式,线程和任务是绑定在一起的,创建4个线程就创建了四份资源
创建线程的第二种方式,线程和任务是分离的,只需要创建一个任务,让四个线程分别去执行
出现线程安全问题的原因:
1,多个线程操作了共享数据num
2,操作共享数据的语句有多条(超过一条),一个线程还没有执行完操作共享数据的多条语句时,cpu就被抢走了
解决:
一个线程得到cpu,全部执行完操作共享数据的多条语句时其它线程才能执行
java提供的解决的方式:同步代码块
synchronized(对象)//对象:锁(锁旗标) 实现了线程之间的互斥
{
(睡着的线程不会释放锁)
需要被同步的代码(操作共享数据的语句)
}
释放锁
同步函数
public synchronized void saveMoeny(){}
同步函数的锁:this
当同步函数为静态函数时,进内存的时候没有对象,只有所属类的字节码文件 E.class,属于Class对象
单例设计模式中懒汉式的多线程并发问题;
饿汉式不存在,因为方法语句只有一条,而懒汉式有多条所有会存在并发问题,可能会new出两个对象
线程间的通信:多个线程执行的任务不同,但是操作的数据相同
同步中至少有两个线程,同步中的线程使用同一把锁
唤醒等待机制:notify(),wait();
wait(),notify(),notifyAll()方法必须用在同步中,因为同步中才有锁,指明哪个锁上的线程去等待或被唤醒
wait():让线程进入等待状态,也就是把线程放入线程池
notify():唤醒的是线程池中的任意一个线程
notifyAll():唤醒的是线程池中的所有线程
wait(),notify(),notifyAll()为什么要定义在Obj里面?
因为wait(),notify(),notifyAll()方法必须用在同步中,因为同步中才有锁,而锁是任意的对象,任意的对象都可以调用的方法只能定义在Object中
生产者消费者
生产者负责生产,有生产任务
消费者负责消费,有消费任务
生产和消费可以同时进行
所以使用多线程
两个生产线程,两个消费线程,程序出现了生产一次消费两次,或生产两次消费一次的问题
出现问题的原因:线程被唤醒之后没有判断标记,直接去生产或者去消费导致问题的发生
解决办法:线程被唤醒之后先去判断标记,把if改成while
把if改成while后,又出现了死锁:原因是当线程唤醒的是本方线程时,会导致所有线程进 入等待状态,从而死锁
把notify()改成notifyAll(),问题解决了,但是不该唤醒的都唤醒了,降低了程序的性能
jdk1.5多线程的实现方式:
jdk1.5有了一个专门描述锁的接口:Lock,把锁面向对象了
Lock的使用方式:
1,创建ReentrantLock的对象,就是创建了一把锁
2,把以前写在同步代码块中的代码写在lock()和unlock()之间
使用Lock替代了同步代码块之后出现了异常,因为wait(),notifyAll()必须用在同步中,而同步没有了所有异常
发现一个Condotion接口,接口中定义了await(),signal(),signalAll()方法,这些方法必须指明锁
使用Lock的newCondotion()方法得到一个与锁绑定的Condition()对象
守护线程:可以看成后台线程,依赖于前台线程,当前台线程全部结束时,即使任务代码没有全部执行完也会立刻结束
设置守护线程,不许在线程启动之前设置
只有一个线程的线程池,任务只能一个一个执行,按照任务的提交先后顺序执行(不好使)
创建有固定个数线程的线程池,任务数量和线程数量相同时,任务会同时执行,任务数量大于线程数量时,多出来的任务会等待线程池中已有的线程空闲下来时去执行(较常用)
IO流:处理设备之间的数据传输
按照方向分:输入流,输出流(相对于内存)
字节流:可以处理任何类型的数据,音频,视频,图片,文本
编码:ASCII,ISO8859-1(欧洲编码),GBK(国标码),UTF-8(通用的编码表)
字符流:专门处理文本的,是基于字节流,在字节流的基础上融入了编码
字节流:
字节输入流父类 InputStream
字节输出流父类 OutputStream
字符流:
字符输入流父类 Reader
字符输出流父类 Writer
FileWriter 文件不存在会自动创建,文件存在会被覆盖
day11
字符流的缓冲区:
缓冲流不具备读或写的功能
只是提高读或写的效率
使用缓冲流必须结合着读流或写流
BufferedReader:缓冲读,readLine()返回的一行不包含换行字符,需要自己加换行println()
BufferedWriter:缓冲写,提供了一个跨平台换行的方法。newline();
装饰设计模式:
基于已经存在的功能提供增强的功能
装饰设计模式的好处:
1,简化了原有体系
2,装饰类还属于原体系
字节流:
InputStream,OutputStream
available()返回的是文件总大小,单位是字节
FileReader,FileWriter
BufferedReader,BufferedWriter
LineNumberReader是BufferedReader的一个小弟
FileInputStream,FileOutputStream
BufferedInputStream,BufferedOutputStream
InputStreamReader
OutputStreamWriter
遇到流的问题怎么分析?
1,确定源和目的
有没有源:Reader,InputStream
有没有目的:Writer,OutputStream
2,确定是不是文本
是文本:Reader Writer
不是文本:InputStream,OutputStream
3,根据第二步确定的再确定设备
源:网络,内存,硬盘,硬盘
目的:网络,硬盘,内存,控制台
例子:
把从键盘的输入的数据写入文件:
1:有源
有目的
2:是文本
Reader,Writer
3:源:键盘 System.in-----》使用转换流,InputStreamReader
目的:硬盘 FileWriter
day12
Properties是一个Map集合,存储的是属性,属性名和属性值必须都是字符串类型的,所以没有使用泛型,可以和流结合使用的集合
打印流:PrintStream:字节打印流,可以使用OutputSream的功能,增加了打印功能
PrintStream ps = new PrintStream("tt.txt");
ps.println(353);
PrintWriter:字符打印流
PrintWriter pw = new PrintWriter(new FileWriter("hh.txt"),true);
pw.println(line);
存储对象:就是对象的序列化
ObjectOutputStream 所存的类需要实现序列化标记接口,此接口无需重写
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));
oos.writeObject(new Person("刘帅",20));
读取对象:反序列化,对象是依赖于其所属的类的字节码文件
读跟存都要.class文件
ObjectInputStream ois =new ObjectInputStream(new FileInputStream("obj.txt"));
Person ren = (Person)ois.readObject();
需要序列化的类最好自己声明static final long serialVeersionUID= ;
transient 关键字//瞬态,不希望参与序列化
RandomAccessFile:不属于IO体系(继承OBJ),只能访问文件,内部既有字节输入流,也有字节输出流,内部有一个字节数组,使用指针操作该数组,从而实现的随机访问
public static void du()throws Exception
{
RandomAccessFile raf=new RandomAccessFile("random.txt","r");
byte[] arr=new byte[4];
int num=raf.read(arr);
int age=raf.readInt();
System.out.println(new String(arr,0,num)+","+age);
long index=raf.getFilePointer();
System.out.println(index);
raf.seek(16);
num=raf.read(arr);
age=raf.readInt();
System.out.println(new String(arr,0,num)+","+age);
raf.close();
}
public static void xie()throws Exception
{
RandomAccessFile raf=new RandomAccessFile("random.txt","rw");
raf.write("刘能".getBytes());
raf.writeInt(56);
long pointer=raf.getFilePointer();//得到指针指向的位置
System.out.println(pointer);
raf.seek(16);//设置指针指向的位置
raf.write("赵四".getBytes());
raf.writeInt(58);
pointer=raf.getFilePointer();
System.out.println(pointer);
raf.close();
}
}
操作8种基本数据类型的流DataOutputSteam,DataInputStream
内存流不需要关
转换流还有一个功能,用构造方法设置编码
InputStreamReader isr = new InputStreamReader(new FileInputStream("utf.txt"),"gbk");
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"utf-8");
Properties pro = new Properties();
//软件启动时加载配置文件的信息到内存
FileReader fr = new FileReader("config.properties");
pro.load(fr);
pro.list(System.out);
//从内存中做修改,配置文件并没有变
pro.setProperty("color","red");
pro.list(System.out);
//把从内存中做的修改写入到文件
FileWriter fw =new FileWriter("config.properties");
pro.store(fw,"qiaojianyong");
合并流:SequenceInputStream
List<FileInputStream> list= new ArrayList<>();
for(int i=3;i<=5;i++)
{
list.add(new FileInputStream("Demo"+i+".java"));
}
Enumeration en = Collections.enumeration(list);
SequenceInputStream sis = new SequenceInputStream(en);
FileOutputStream fos =new FileOutputStream("hebing2.java");
byte[] arr=new byte[1024];
int num;
while((num=sis.read(arr))!=-1)
{
fos.write(arr,0,num);
}
操作8种基本数据类型的流DataOutputStream,DataInputStream
DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
dis.readInt()
dis.readBoolean()
DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
dos.writeInt(66);
dos.writeBoolean(false);
day13
使用udp实现数据的发送
1,创建Socket端点实现数据的发送
2,明确发送的数据
3,把数据封装成数据报包,指明接收的端口
4,使用socket的发送功能发送数据
使用UDP实现数据的接收
1,创建Socket端点实现数据的接收
2,创建一个空的数据报包来接收数据,要指明端口号
3,使用Socket的接收功能来接收
使用TCP实现数据的发送
1,创建Socket端点,负责数据发送,同时需要和服务端连接
2,
反射:动态获取类也就是字节码文件
得到字节码文件对象的方式
1:Object中的getClass方法
需要创建对象
2:每种数据类型都有一个class属性
该属性返回的就是字节码文件对象
3:使用Class中的静态方法forName()
day14
一张表对应同一类实体
一张表就是一个关系
实体和实体之间是存在关系的,所以表和表之间就存在关系,所以称为关系型数据库
空值null不是值,表示未知
‘’空值是个值
删除表中记录
一条一条的删,速度慢,删除的数据可以恢复
DELETE FROM emp;删除表中所有记录
先把整个表删除,然后再创建一张相同的表,数据不可恢复
TRUNCATE TABLE emp;
查询的结果是以表的形式展示,并不是真正的表
模糊查询:like _:匹配任意一个字符 %:匹配任意多个字符
select distinct deptno,mgr from emp去重
任何类型和null进行运算最后结果是null
order by age desc 降序排序
用group by时select后面只能写分组的依据和聚合函数
分完组后再筛选的要用having
day15
表中一条记录对应一个实体,实体是独一无二的,所以表中记录也必须是独一无二的
实体完整性:保证表中每条记录唯一,primary key,unique,auto_increament
被设置为unique的字段上的值唯一,但是可以为null
域完整性:对某个字段上的取值范围的限制,保证字段上的取值准确,数据类型,not null,default
引用完整性:保证一张表中某个字段上的数据是另一张表中某个字段上存在的数据,外键foreign key(sid) references students(id)
外部增加外键 alter table score add constraint fk_score_stu(名字) foreign key(sid) references students(id)
union会去除重复记录
多表查询
union 取两个查询结果的并集。会出现重复记录
外部删除外键 alter table score add foreign key fk_score_stu
多表查询
99查询法,同时查询两张表和有没有主外键关系没关系,只要两张表有相同的字段就行
外键只是保证数据准确的
连接查询:内连接,外连接:左外连接,右外连接
内连接:
select * from students inner join score on score.'sid'=students.'id'
左外连接会吧左表中的记录全部查询出,如果在右表中没有满足on后面条件的记录则显示null
select * from students left outer join score on score.'sid'=students.'id'
在标准写法中on后面只能写连接条件,其他条件写在where后面
查询每个部门工资最高的员工的员工编号,员工姓名,部门编号,部门名称(自己写的)
SELECT e.empno,e.ename,d.deptno,d.dname FROM emp e LEFT OUTER JOIN dept d ON e.deptno=d.deptno WHERE e.sal IN (SELECT MAX(sal) FROM emp GROUP BY deptno);
-- 有2个以上直接下属的员工信息
SELECT * FROM emp WHERE empno IN(SELECT mgr FROM emp GROUP BY mgr HAVING COUNT(*)>2)
day16
测试方法不能有返回值类型,不能有参数
@Test
public void test1(){
}
'"+username+"'
漏洞,sql注入 1' or '1'=1'
xml语法
CDATA:里面的数据会原样显示
*<![CDATA[
内容
]]>
web
web-inf
下面的jsp是不能被访问的
类路径在web-inf下的class。。。。文件下
servlet 的生命周期:Servlet被创建实例到被销毁的过程
每个servlet只有一个实例且在服务器上
1.实例化:tomcat容器负责创建实例
2.初始化:执行init方法(生命周期中只执行一次)
适配器模式:
自己写的接口,方法太多时,为了提高用户的使用体验,自己加一点实现类
3.当有客户端请求的时候,执行service方法
4.服务器停了会被销毁
day17
对每个客户端会生成相应的httpservletrequsert对象和httpservletresponse对象
httpservletrequsert:封装的是客户的请求信息
httpservletresponse:封装的是响应的信息
tomcat容器负责创建httpservletrequsert、httpservletresponse这两个对象
ServletConfig:每个servlet对象都有相应的servletconfig对象,而且该对象由容器创建,
获得初始化参数在xml里面
第一种方法:我们通过初始化函数得到servletconfig对象
第二种方法:ServletConfig config =this.getServletConfig();
第三种方法
httpservlet继承了GenericServlet,GenericServlet实现了ServletConfig接口
直接可以获取值,不用通过config对象
String v =this.getInitparameter("mun");
ServletContext:上下文对象,同一个用下的所有servlet共享该对象,由容器创建该对象
作用:1:实现数据共享
2:获取全局的配置信息
3:得到任何资源的全路径
设置了服务器的编码的同时告知了浏览器使用的编码
response.setContentType("text/html;charset=utf-8");
直接通过this.getServletContext()获得
context.setAttribute("键","值");
在别的servlet也能得到该上下文
处理get方式乱码:1.自己用再编码再解码。2,该变tomcat,URIEncoding="utf-8"
编码的设置:
response.setCharacterEncoding("utf-8");//设置服务器端使用的编码
response.setContentType("text/html;charset=utf-8");//设置了服务器端使用的编码同时告知浏览器使用什么编码
cooke(-1):默认值是-1,表示保存在浏览器缓存中,浏览器关闭时cookie就没了
cookie.setMaxAge(0):值为0,表示删除cookie,即使浏览器不关闭,cookie也没了
cookie.setMaxAge(60*5):cookie会被保存到磁盘文件中
day19
findAttribute方法会依次从page,request,session,application中找,先找到就用谁
EL表达式默认依次从page,request,session,application中找,如果找不到显示"",找不到不会报异常
day21
DButil 封装的数据库查询方法
QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
//适合查询结果是一条记录的,把这条记录的数据封装到数组中
Object[] result = qr.query("select * from users where id=?", new ArrayHandler(), 10);
//适合查询结果是多条记录的,把每条记录封装到一个一维数组中,再把所有数组放到一个List集合中
List<Object[]> result = qr.query("select * from users", new ArrayListHandler());
//适合得到某个字段上的所有的值,ColumnListHandler的参数指明取第几列上的值
List<Object> result = qr.query("select username,psw from users", new ColumnListHandler(1));
//适合获取单行单列的值(多用于聚合函数)
Object v = qr.query("select count(*) from users", new ScalarHandler(1));
//把每条记录封装到一个map中,再把这个map封装到另一个map中,另一个map的键就是KeyedHandler的参数决定的
Map<Object, Map<String, Object>> r = qr.query("select * from users", new KeyedHandler(1));
//适合查询一条记录,把这条记录封装到map中
Map<String, Object> r = qr.query("select * from users where id=?", new MapHandler(),2);
//适合查询多条记录,把记录封装到map中,再把map放到list中
List<Map<String, Object>> r = qr.query("select * from users", new MapListHandler());
//把一条记录封装到一个User对象中
User user = qr.query("select * from users where id=?", new BeanHandler<User>(User.class),2);
//使用的是set方法进行赋值,要保持set方法后边的名字和表中字段一致,与实体类中属性名无关
List<User> users = qr.query("select * from users", new BeanListHandler<User>(User.class));
//极不常用
List<User> users = qr.query("select * from users", new ResultSetHandler<List<User>>()
fmt 属于jstl标签库的,用于数据的格式
拦截器MethodIntercepton,拦截器和过滤器的区别,拦截器和过滤器执行顺序是什么?
springAOP面向切面原理是(动态代理) 动态代理和静态代理区别,动态代理在哪用到了
servlet是单实例的,会存在线程不安全问题(并发修改了成员变量),不使用成员变量来避免这种不安全
过滤器和拦截器的区别:
①拦截器是基于java的反射机制的,而过滤器是基于函数回调。
②拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
③拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
④拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
⑤在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
⑥拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。
单例的模式,会被怎样攻击(反射),怎么解决
Class<?> classType = Elvis.class;
Constructor<?> c = classType.getDeclaredConstructor(null);
c.setAccessible(true);
Elvis e1 = (Elvis)c.newInstance();
Elvis e2 = Elvis.getInstance();
System.out.println(e1==e2);
输出false
会被反射攻击,反射可以得到私有的构造方法,预防的方法
在构造方法中判断对象个数,超过一个就抛出异常
private ElvisModified(){
synchronized(ElvisModified.class)
{
if(flag == false)
{
flag = !flag;
}
else
{
throw new RuntimeException("单例模式被侵犯!");
}
}
}
从jdk1.5开始可以用这种新的单例写法也可以防止反射的攻击,只需编写一个含单个元素的枚举类型:
public enum SingletonClass
{
INSTANCE;
public void test()
{
System.out.println("The Test!");
}
}
垃圾回收是什么,垃圾回收机制的算法,新生代,老年代,java会不会存在内存溢出?怎么溢出?怎么避免,能不能主动调用垃圾回收器
集合了解吗?你给我简单的介绍一下,list你具体说,arrlist和list的区别,底层,vertor,的底层,线程安全,为什么?(有锁),concurent 线程安全又快的类,coyponright
数组和集合的区别,一个定长一个不定长
ajax的跨域访问方法,(jsonp.......)
缓存redis?
阻断式模式,优化性能
如何处理高并发问题
1、分布式(不同系统负责不同功能)
2,、集群(不同主机存储不同数据)
3、主从(完成高可用)
4、数据库读写分离(灵活设置事务隔离级别
5、缓存(redis)
6、nginx(反向代理、负载均衡)
7、图片服务器(静态资源服务器)
8、CDN
9、图片编码:Base44
10、压缩前端代码
11、数据库分片
XSS攻击 CSRF攻击
mysql各种函数
mybatis 如何实现批量插入
spring
di:Dependency Injection,依赖注入
ioC:Inversion of Control 控制反转
AOP:Aspect Oriented Programming 面向切面编程
雪崩 穿透
mybitiys 拦截器
sql行转列