当前位置:   article > 正文

java 基础总结(仅本人使用)_remove(k key,v value),返回什么

remove(k key,v value),返回什么

方法

构造方法

规则

每个方法都有默认的Object构造方法
但是只要重写它的构造方法后 默认的构造方法将失效
·
·
·

设计

可以带参
*优点 创建对象的同时类中的属性进行赋值
可以不带参数
*优点 只是想调用里面的方法的话 就不加参数 方便创建对象

*new一个方法的时候就是在调用它的构造方法
·
·
·

方法重写与重载

重载
*在同一类中 方法名相同 参数个数不同 或者参数类型不同 或者 参数顺序不同

*重写
一般在子父类继承关系中 出现 或者实现接口的时候会写方法
·
·
·

方法

方法可以带返回值 可以不带返回值 可以带参数(带参的叫形式参数) 可以不带参数
·
·
·

面向对象

面向对象概念

什么是类
*具备相属性和方法的一类对象的集合
什么是对象
*真实存在的实体
之间的关系
*类是对象的抽象话,对象是类的实例化
由对象去编写类,再由类去new对象
·
·

全局变量和局部变量的区别

全局变量
*类下面的叫全局 可以不赋初始值 系统会有一个默认的初始值 (0或null);
局部变量
*方法下面的叫局部 创建时必须赋值 不然会报错
·
·
·

三大特性

封装

继承

概念
*描述类与类之间的关系 被抽取的方法和属性的类就是子类 新生的类就是父类
结果
*子类具备父类的方法和属性
*子类必须全部重新父类的方法
关键词
*extends
特点
*满足 is—a的关系 (例如 铅笔是文具);
·
·
缺点
*java当中只能单继承 (一个子类只能有一个父类 一个父类可以有多个子类)
·
·
·

this 和 super

this
*1、普通直接引用当前对象本身
2、this()在同一类内调用其它方法。
3、引用构造方法 ,this(参数) ,应该为构造函数中的第一条语句,调用的事1本类中另外一种
形式的构造方法
super
*1.只能在子类的构造方法中调用父类构造方法
2.只能在子类构造方法的第一句话
3.语法super(可以带参)
4.当子类没有定义构造方法时 默认调用父类的无参构造方法 此时如果父类的无参构造方法没
有或者失效 导致子类调不到父类的无参而报错
5.子类调用带参父类构造方法 一般在子类构造方法中准备形参接收实参传递给父类构造方法中
的参数列表
·
·
·

多态

核心
*父类的引用指向子类的对象
注意
*1、使用这个父类的引用 可以调用出父类中出现的属性和方法 如果子类重写了父类的方法 则调用重写后的方法
2、父类的引用只能调取共有的方法 想调取子类特有的方法就必须强转换成子类的对象
一般判断用 instanceof 返回bollean类型
用法
·
·
*多态继承中用法:
&&多态用于方法的返回值
*方法返回的是一个父类的引用 里面装的是一个子类对象
当不能确认返回哪个子类对象的时候 可以返回 父类的引用
&&多态用于方法的参数
*形参为一个父类的引用 可以接收任意一个子类的对象
·
·
·

抽象类

抽象类注意事项

①不可被实例化
②抽象类有构造器(凡是类都有构造器)
③抽象方法所在的类,一定是抽象类
④抽象类中可以没有抽象方法。
·
·

基础概念

*关键词abstract
*不能被new成对象
*可以被继承 想用里面的方法就必须被重写
·
·

访问修饰符

开放等级  public     protected(受保护的)     (default)         private 
*父类的成员变量和成员方法至少需要 protected及以上的访问修饰符来修饰 才能被子类对象访问
*使用protected修饰的类的成员变量和成员方法 只能被子类访问 其他类访问不到 属于受保护的
*什么修饰符都不写 在不同的包下不能访问 在相同的包下可以访问
  • 1
  • 2
  • 3
  • 4

·
·

接口

设计接口
*关键字 设计时用 interface 实现类用 implements去实现接口
*满足 has–a 的关系(例如 usb接口有鼠标)
·
优点
*可以以间接的形式实现多继承

API

Sring类的方法

.equals(String); 返回boolean
.equalsIgnoreCase(String); 返回boolean
.charAt(int); 返回char
.compareTo()返回int 不忽略大小写
.compareToIgnoreCase() 返回int 忽略大小写
.length() 返回int 获取字符串的长度
.isEmpty() 返回boolean 判断是否包涵
·
·

下标找元素

.subString() 返回元素(String) 根据下标找元素
.subString(int 起始,int 结束下标+1) 截取起始到结束下标之间的字符串
.charAt(下标) 返回char类型
·
·

元素找下标

.indexOf() 返回int 根据元素找下标
.indexOf("",int fromIndex) 返回int 从fromIndex这个位置开始找 第一次出现要查找的值的下标
.lastIndexOf("") 从尾部开始向前找 第一次出现要查找的值的下标
.lastIndexOf("",int fromIndex) 从fromIndex这个位置向前找 第一次出现要查找的值的下标
·
·
·
.contains(String) 返回boolean 判断是否含有某个字符串
.startswith(String) 返回boolean 判断是否以某个字符开始
.endswhth(String) 返回boolean 判断是否以某个字符结束
.startsWith(String,int startIndex);
·
·
.trim() 去开头和末尾的空格(中间的不会去掉)
·
.replace(String【char】oldStr,String【char】newStr) 返回String类型 左侧开始查找 替换第一个
.replaceAll(String oldStr,String newStr) 左侧开始查找 全部替换
.replaceFirst(String oldStr,String newStr) 左侧开始查找 替换第一个
·
·
.split(String s) 返回String【】类型 把一个字符串以s来切割 每个部分为生成的String数的一个元素
如果开头就是切割标记 会多出一个空元素 如果结尾是切割标记 直接砍掉
.join(“连接标记”,String[])

String高级类

方法

.append(下标); 在下标后添加字符串
.deleteCharAt(下标); 删除下标的元素
.delete(起始下标, 结束下标+1);
.replace(起始下标, 结束下标+1, String );
.insert(插入下标, Object);
.reverse() 将内部字符重新排序 逆序
.toString() 转换为String类型

StingbBuilder

线程安全的 效率低
·

StringBuffer

线程非安全的 效率高

底层数据结构

*底层默认16个字符的数组 char[]
*如果初始值超出了默认长度 再+16的char数组 保存值
如果修改时 值超出了长度 则进行数组扩容 创建新的数组 长度为原长度2+2 老数组的值赋给新
数组 指针指向新数组

Math类

方法

Math.abs() 返回int 求绝对值
Math.ceil() 向上取整
Math.floor 向下取整
Math.pow(double a,double b) 返回double a的b次方
Math.sqrt() 开平方
Math.random() 随机数 【0,1)
Math.round()
正数 +0.5
负数 +0.5 负数是5舍6入
·
·

BigDecimal类

//创建BigDecimal 方式一:
double d = 5.578;
BigDecimal bd = new BigDecimal(Object); //会出现无尽小数
//创建方式二 推荐方式
BigDecimal bb = BigDecimal.valueOf(double\long);
·
·

DecimalFormat类

DecimalFormat df = new DecimalFormat(“0.0000”);
例 System.out.println(df.format(6.45678)); //最终返回的是字符串 四舍五入 不够会补零
·
·

Date日期类

Date date = new Date(); //返回当前时间
Date date = new Date(可以带参数);
·
·

*System.out.println((date1.getYear()+1900)+“年”+(date1.getMonth()+1)+“月”+date1.getDate()+“日\t”+date1.getHours()+":"+date1.getMinutes()+":"+date1.getSeconds()+"\t星期"+date1.getDay());
·
·
System.currentTimeMillis(); 从1970年1月1日0点到当前时刻的时间差 毫秒 long
·
date.getTime(); 计算从1970年1月1日0点到这个时间对象的时间差
·
·
//定义格式 年yyyy 月MM 日dd 时HH 分mm 秒ss
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy年MM月dd日 HH:mm:ss”);
·
使用模板对象去格式化日期对象 返回一个字符串
String dateString = sdf.format(date1);
System.out.println(dateString); //2020年10月13日 16:26:16
·
·

Calendar

创建过程
*Calendar c =Calendar.getInstance();
获取某个属性值 属性使用类名.属性名 例如:Calendar.YEAR
*System.out.println(c.get(Calendar.YEAR));
*System.out.println(c.get(Calendar.WEEK_OF_MONTH));
给某个属性赋值
*c.set(Calendar.YEAR, 1999);
获取某个时间之后的日期
*c.add(Calendar.MONTH,2);
·
·

包装类

概念
*针对于8个基本数据类型 java提供了对应的引用类型 被称为包装类(封装类)
有了包装类 就可以以面向对象的方式类操作数据(属性 方法)
·
byte ——Byte
short——Short
int————Integer
long————Long
double————Double
float————Float
char————Character
boolean———Boolean
·
·
Integer i = new Integer(int) //把基本类型 装箱
int ii = i.intValue(); //吧包装类的数据 拆箱

// jdk1.5以后 可以自动装箱和拆箱
Integer newI = 5;
int newII = newI;
·
·

类型转换

	Byte.parseByte("123");
	Short.parseShort("123");
	int num = Integer.parseInt("123");
	Long.parseLong("123");

	Float.parseFloat("123.2");
	Double.parseDouble("123.2");
	
	Boolean.parseBoolean("true");
	Character不具备parse方法 可以直接使用charAt(下标)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

·
·
·
·

集合框架

框架:整合的工具
集合:数组 数列 管理和操作批量数据的方案 提供了多种多样的方法

集合与数组的区别

数组

长度固定  同类型的数据  
  • 1

集合

不限定长度 可以真正的保存不同类型 保存的都是对象  包装类
  • 1

集合关系图

在这里插入图片描述

Collection接口

方法

.add(对象) 添加对象
.addAll(对象集合)相当于复制集合
.clear()清除所有对象
.contains(对象) 判断是否包含这个对象
isEmpty() 是否有对象
Iterator() 返回当前集合的迭代器对象
remove(对象) 删除某个对象
size()集合元素的个数
toArray() 将集合转换为一个数组
set(int index,对象)替换掉对象

·
·

Set集合

ArrayList集合

线程不安全的 效率高 查询快 增删慢
·
底层结构
*底层是一个数组结构

  • 使用无参构造时 默认开辟一个长度为10的数组

*使用带int类型的参数的构造方法 参数为自定义的长度
·
*如果大于0 则扩容其1.5倍
集合保存的最大值 MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
如果超出 则报 OutOfMemoryError错误
·
·
*特点:因为在增删过程中 会新建新数组 导致老数组称为垃圾内存
建议在新建时 预估集合大小
·
·

迭代器

Iterator

Itorator ii = 集合名.iterator();
有些集合没有索引 不能使用普通 for 遍历
Iterator 和 增强for循环可以遍历所有的集合
·
·

ListIterator

LIstIterator ii = 集合名.ListIterator(al.size);//让指针调到最后
while(ii.hasprevious()){
int i = li.previous();
System.out.print(i+"\t");
}

LinkedList集合

特点:双链结构 增删效率高 查询效率低

与数组区别:不需要频繁的进行空间的开辟 数组可以直接通过索引找到对应的元素 而链表从头开始一个一个查找 LinkedList集合没有默认长度 也没有扩容的方法
·
·

方法

add(int index,对象)
addFirst(对象)
addLast(对象)
getFirst()
getLast()
removeFirst()
removeLast()
pop() 将第一个元素移除 返回这个元素
push(对象) 将一个元素添加到头部

offer(对象)		将对象添加到尾部
offerFirst(对象)	将对象添加到头部
offerLast(对象)	将对象添加到尾部

poll()			删除头元素
pollFirst()		删除头元素
pollLast()		删除尾元素
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

·
·
·

比较器

基本数据类型 和String类型 不需要去重写比较规则
比较器方案一:
直接在待比较的实体类中添加比较规则
1.实体类实现接口
public class User implements Comparable//跟上泛型类型
2.重写compareTo方法 再编写比较规则
3.使用比较器
数组 Arrays.sort(数组名) 对于数组只能重写 COmparetor的方法 基本类型的时候要用包装类
集合 Collections.sort(集合名,)
·
·
·

Set接口

*无索引 无序 不能储存重复的数据
*对于基本数据类型 和 String类型 因为都重写了equals()和 hasCode()方法 所以可以实现制动去重。
*对于自定义类型 同时 存储方式为HashSet、LinkedHashSet 我们如果需要只判断值 name就需要在自定义类中去重写这两个方法 equals() hashCode()方法。
*对于TreeSet 存储任何类型都会自动去重 但是需要添加比较器 否则报错。
·
·

总结

List接口

展示顺序
*默认情况下 储存顺序与展示顺序一致
*可以通过添加比较器 在通过Collection.sort() 实现元素的出现排序
·
·

Set接口

默认情况下 存储顺序和展示顺序不一致
HashSet\LinkedHashSet
不能通过添加比较器 固定元素顺序
除非将其转成其他可以排序的集合
(转换语法 ArrayList au = new ArrayList(HashSet集合名) )
基本数据类型和String类型 都是自动去重
自定义类型
HashSet\LinkedHashSet 需要重写equals()和hashCode() 否则不能去重
TreeSet 自动去重 (注意要添加比较器 不然保存失败)

TreeSet

*对于基本数据类型和String类型 默认升序
*对于自定义类型 需要添加比较器 (否则保存时报错ClassCastException) 按照比较器规则排序
*不需要通过Collections
*如果实现Comparable直接迭代展示即可
*如果实现Comparator需要在创建集合时 将比较器对象作为构造方法的参数传入

·
·
·

Map接口(双列集合<K,V>)

*该接口下的集合有两个泛型 第一个泛型确定键的类型 第二个泛型确定值的类型
*是以键值对的映射的形式来保存数据
*键类似于索引 不能重复 值可以重复
·
·

实现类

HashMap : 可以储存null 无序的 线程非安全的
LinkedHashMap: 存储顺序与展示顺序一致 (Linked相当于给集合添加一个链条)
HashTable: 不可以存储null 线程安全的 古老的散列集合 效率低

创建语法

HashMap<String,Integer> ii = new HashMap<>();
·
·

常用方法

.put(K Key,V value)添加键值
.get(K key) 获取K值
.remove(K key) 返回value值
.remove(K,V) 返回bollean类型
.repleace(K key,V value)返回v 修改v值
.replace(K key,V oldv,V newv) 返回boolean
.size() 返回int类型 集合的长度
.clear() 清除集合
.containsKey(键名) 返回boolean 判断结合是否存在这个键
.containsValue(值) 返回boolean
·
·

遍历

Set .keySet()
Collection .values()

entry:键值对的映射关系(包含了键和对应的值 的映射)
Set<Entry<键,值>>	.entrySet()

//通过这个方法 返回键值对映射 
//集合中每个元素 都是一个Entry对象 包含一个键值对
	Set<Entry<String,Integer>> entrySet = hsi.entrySet();
	for(Entry e : entrySet) {
		System.out.println(e.getKey()+"\t"+e.getValue());
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

·
·
·
1.1 for each map.entrySet()
Map<String, String> map = new HashMap<String, String>();
for (Entry<String, String> entry : map.entrySet()) {
entry.getKey();
entry.getValue();
}

1.1 for each map.entrySet()
Map<String, String> map = new HashMap<String, String>();  
for (Entry<String, String> entry : map.entrySet()) {  
    entry.getKey();  
    entry.getValue();  
}
1.2 显示调用map.entrySet()的集合迭代器
Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();  
while (iterator.hasNext()) {  
    Map.Entry<String, String> entry = iterator.next();  
    entry.getKey();  
    entry.getValue();  
}

1.3 for each map.keySet(),再调用get获取
Map<String, String> map = new HashMap<String, String>();  
for (String key : map.keySet()) {  
    map.get(key);  
}

1.4 通过Map.values()遍历所有的value,但不能遍历key
for (String v : map.values()) {    
   System.out.println("value= " + v);    
}		在这里插入代码片
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

代码块

概念: 在程序中使用{} 定义起来的一段程序
分类
普通代码块:{} 里面声明的变量不能再外部使用
构造代码块:{}写在实体类中任意位置 优先于构造方法 每new一次 执行一次
静态代码块:{}写在普通类中 优先与构造代码块 程序运行过程中 只在开始运行一次
同步代码块:线程时会遇到

执行流程

程序必须先有main() 否则运行报错
	执行主类静态代码块 只执行这一次
	
	再回到main()依次执行
	
	当new对象的时候 :
		执行该类中的静态代码块 只执行这一次
		执行构造代码块
		再执行构造方法
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

静态代码块

概念 : 可以用于全局属性和全局方法的声明 可以避免对象实例化的限制
*由static修饰的全局属性 被该类所有的对象共享 这样的属性保存在全局数据区中(方法区保存的元数据)
特点:
*由static修饰的成员变量和成员方法 可以由类的对象访问 也可以由类名直接访问
!!!(接口中的静态方法只能被接口类名访问 不能被接口实现类对象访问)
·
·
·

注意事项

*由static修饰的方法中不能有this关键字
*由static修饰的方法中 可以调用有static修饰的方法和属性
*可以调用普通方法和普通变量
*如果要在这个方法中调用普通方法和普通全局变量 则需要先new该类的对象 对象去调用
*由非static修饰的方法中 可以调用由static修饰的方法和属性

final (最终的)

final修饰的量叫常量 值是不可变的
例 final int NUM = 5;

常量的赋值

建议 使用静态常量 并写常量名大写
建议 使用 静态代码块来包裹大批量的常量
对于局部常量来说 可以通过方法的形参赋值
对于全局常量来说 必须在声明时赋初始值 否则报错
用final修饰的方法不能被重写

final修饰的类,为最终类,该类不能被继承
final修饰的方法可以被继承和重载,但不能被重写

·
·
·

异常类

概念:程序运行过程中出现的非正常情况 导致程序非正常停止

异常体系

所有异常的共同父类 Throwable
两个子类 Error Exception
·
Error 比较严重的 大部分属于系统方面的 错误 编码无法处理
Exception 可以通过修改代码来进行纠正的问题
·
编译期异常:检查异常 checked异常
例如:类型转换.parse() ParseException IO流IOException JDBC创建连接等
·
运行时异常:RuntimeException编译可以正常通过 运行时根据具体情况 看是否报出异常
·
例如:ArrayIndexOutOfBoundsException 数组越界
NullPointerException 空指针异常
·
处理解决异常:
throws:在方法参数列表的后面 通过throws抛出可能的异常类 多个异常类用,隔开 不需要考虑顺序
如果b方法调用了抛出异常的a方法 则b方法也要通过throws抛出对应的或更高级的异常类

	try{..}catch(异常类 对象名){..}..finally{..}:把可能出现异常的语句放在try模块中 当真的出现异常时 会去匹配以下的catch模块 找到对应的异常对象 则进入执行自定义的一些代码 无论是否出现异常 都一定会执行finally模块	当一个try..catch模块抛出多个异常时 同等级异常无所谓顺序 如果有更高级的异常 要放在后面
	
throw:
如果是编译期异常 无论是否使用了throw 都必须要去选择throws或者try..catch模块 处理
如果throw抛出的是运行时异常 依然可处理可不处理
*使用在方法的内部 
*throw后抛出的是一个异常对象 一般可以使用匿名对象 在构造方法中重新覆盖message信息

在继承关系中
	
	父类方法编写时 抛出了异常 则子类在重写这个方法时 
		**如果内部调用了父类的方法 则外部需要抛出和父类一致的异常 
		**如果内部没有调用父类的方法 可以不抛或者抛出小于等于父类编译异常级别的异常 也可以抛出任意运行时异常
	父类方法编写时 没有抛出任何异常 则子类重写方法时不能抛出编译异常 可以抛出运行时异常
		1.重新写个子类特有的方法
		2.父类方法加上异常
		3.在子类中通过try..catch模块处理异常
		
	接口中的方法与实现类重写方法 参照继承关系
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

自定义异常类

1.创建一个类 继承一个异常的类型 
			继承Exception 编译异常
			继承RuntimeException 运行时异常
			//定义了一个自定义检查时异常
	public class newException extends Exception/继承RuntimeException {
        
	}
	2.给异常类中添加一个带字符串参数的方法 字符串作为当前异常message的值
		public newException(String txt) {
            super(txt);
        }
    3.找到需要抛出此异常的位置 通过throw抛出这个异常
			throw new newException("不能使用张三");
		1》如果抛出的是编译异常 一定要再次通过throws/try..catch 二选一去处理这个异常
		2》如果抛出的是运行时异常 可处理 可不处理 如果处理 依然是二选一

	4.//抛异常
			try {
				throw new NewRuntimeE("张三不可用");
				//遇到throw 是一定要抛出对应异常的 只是分情况看是否需要处理和采用哪种方案处理
				//拿着个抛出的异常对象去catch里匹配  匹配成功后执行对应代码
				
			} catch (NewRuntimeE e) { //一定要放一个匹配的异常 接收抛出的异常对象
				// TODO: handle exception
				System.out.println(e.getMessage());
			}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

泛型

<E>
·
  • 1
  • 2

ArrayList al = 。。。。 集合确定类型是用泛型来约束

设计带泛型的代码
使用带泛型的代码 要明确泛型的具体类型

&& 在设计类的时候 使用泛型
	public class 类名<E,K>{ //设计定义泛型
		在类的内部 就可以使用这个类型
		E age;
		K price;
	}
&& 方法中设计使用类中已定义的泛型

	public K show(E name) {
		System.out.println(this.age);
		System.out.println(name);
		return price;
	}
	&& 方法中设计使用类中已定义的泛型

	public K show(E name) {
		System.out.println(this.age);
		System.out.println(name);
		return price;
	}
	&& 静态方法中定义和使用泛型
	public static <A> void showJ(A name) {
		if(name instanceof Integer) {
			Integer n = (Integer)name;
			System.out.println(n+5);
		}
	}
	根据调用静态方法时 真正传入的实参的类型 来确定泛型的类型
	&& 接口中泛型的设计和使用
    public interface Eat<E> {
        public void eat(E name);
    }
    方案一:接口中的泛型只能从实现类中的泛型去选择
    public class User<J,E,K> implements Eat<K> {
    	@Override
        public void eat(K name) {//只能使用接口中选择的泛型类型
            // TODO Auto-generated method stub
        }
    }
    方案二:在实现类中直接定义接口的真实类型
    public class User<J,E,K> implements Eat<Integer>{
    	@Override
        public void eat(Integer name) {//只能使用接口中使用的泛型类型
            // TODO Auto-generated method stub

        }
    }
    && 泛型通配符
	?:代表一个泛型类型 
	用于已经定义了泛型 在中间的使用环节中 还不能确定最终类型的时候 用?来表示他拥有泛型
	最终调用的时候 再来给泛型赋真实的类型
	中间调用环节:
	public void show(User<?,?,?> u) {
	}
	最终调用环节:
	User<Integer,String,Double> u = new User<Integer,String,Double>();
	Util util = new Util();
	util.show(u);
	
    也可以在中间使用环节中 直接定义上真实的类型 这样的话 最终调用时 必须传相同类型
    中间调用环节:
	public void show1(User<Integer,Double,?> u) {
	}
	最终调用环节:
	User<Integer,String,Double> u = new User<Integer,String,Double>();
	Util util = new Util();
	util.show(u);//类型不匹配 会报错
	
	泛型受限
	1任何定义了泛型的类型 2在中间使用环节 当想对这个泛型进行限制 就可以使用受限泛型
	
	定义两个方法 参数为不确定类型的集合 
		public void show2(Collection<? extends User> u) {
			//集合的类型 是User的子类 可以放入本类和子类
		}
		public void show3(Collection<? super User> u) {
			//集合的类型 是User的父类  可以放入本类和父类
		}
		 
		//放入User的父类作为集合类型 U3 User U1 U2
		ArrayList<U3> a3 = new ArrayList<U3>();
		Util u = new Util();
		u.show3(a3);
		//u.show2(a3); 报错
		
		//U1是User的子类 所以符合show2方法参数的要求
		ArrayList<U1> a1 = new ArrayList<U1>();
		u.show2(a1);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88

·
·
·

内部类

*在一个类的内部 直接定义了另外一个类 作为成员类
*在一个类的内部的成员方法中 定义一个类 方法内的局部内部类

  • 从设计角度来说 不建议使用内部类 内部类只是更加明确了 类与类之间的
    关系 并不利于代码复用
  • 从后期使用上来说 调用过程能够适当节省代码 减少单独创建类造成的空间浪费
class Classes{
		String classname;
		public void show(){
			int score;
			class Teacher{
				String teacher;
			}
		}
		class Student{
			String name;
		}
		
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

成员内部类

*成员内部类中的方法中 可以通过this调用该内部类的属性和方法
						可以通过外部类类名.this.外部类的属性和方法
  • 1
  • 2
//一:成员内部类   外部类:className show()
		public class Student{
			public String stuName = "张三";
			public void show() {
				System.out.println(this.stuName);
				//内部类的方法调用外部类的属性
				System.out.println(Classes.this.className);
				//内部类的方法调用外部类的方法
				//Classes.this.show();
			}
		}
		
		调用过程:
			1.成员内部类可以被外部类的方法调用
				//外部类的成员方法  去掉用内部类的属性和方法 
		//外部类的show()方法中 创建内部类的对象
		public void show() {
			//System.out.println(this.className);
			Student stu1 = new Student();
			System.out.println(stu1.stuName);
			//stu1.show();
		}
			2.可以在测试类中 通过外部类的对象去new内部类的对象 调用内部类自己的方法和属性
		//一:成员内部类的调用 使用外部类的对象去new内部类的对象		
		new Classes().new Student().show();
		//new Classes().show();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

&&更多使用场景:接口

* 通常 需要创建实现类去实现接口的方法 最终也是通过实现类的对象去执行重写后的方法
  • 1
//使用匿名内部类来实现接口的方法  省略了实现类的创建
		FunctionInto fi = new FunctionInto() {
			@Override
			public void show(String name) {
				// TODO Auto-generated method stub
				System.out.println(name);
			}
			
			@Override
			public int getNum() {
				// TODO Auto-generated method stub
				int num = 100;
				return num;
			}
		};
		//使用接口的引用去调用实现的方法
		fi.show("张三");
		System.out.println(fi.getNum());
		
		//使用匿名对象的方式 直接调用内部类实现的方法 特点:只调用一次
		//因为本代码中getNum()返回int类型 所以前面用了一个int变量接收
		int n = new FunctionInto() {
			@Override
			public void show(String name) {
				// TODO Auto-generated method stub
				System.out.println(name);
			}
			
			@Override
			public int getNum() {
				// TODO Auto-generated method stub
				int num = 100;
				return num;
			}
		}.getNum();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

总结

没有实现类的接口 接口中有抽象方法(先不考虑抽象方法是否有参数和返回值)
1.直接在方法中使用接口 使用内部类实现这个接口 接着调用接口的抽象方法
·
·
2.接口作为方法的参数 在测试类中 最后调用方法时实现接口
抽象方法带参数 可以由外部方法准备一个值(常量或者参数)
抽象方法带返回值 正常接收操作
·
·
3.接口作为方法的返回值 需要在设计这个方法时 在返回之前去实现接口 获取接口的实现类对象作为返回值
抽象方法带参数 先调用外部方法获取到接口实现类对象 再调用抽象方法的时候传入参数
抽象方法带返回值 先调用外部方法获取到接口实现类对象 再调用抽象方法按照有返回值的方案操作
·
·
·

IO流

常用方法(file)

.length()  返回long  文件中内容的大小        /1024  才是kb       
.exists()   返回boolean 判断当前路径文件是否存在
.createNewFile() 返回boolean   创建文件
.mkdir()	返回boolean     创建一个文件夹			
.mkdirs()	返回boolean     创建多级文件目录		
.isDirectory()	判断是否是一个文件夹
.isFile()		判断是否是一个文件
.delete()		删除
.listFiles()	  返回一个数组保存某个文件夹下所有的文件信息  File[]
.list()			返回文件名 String[]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

·
·

关系图

在这里插入图片描述

字节流

写出操作

OutputStream —>FileOutputStream实现类
FileOutputStream fos = new FileOutputStream(File/String path,boolean);
.close()
.flush() 刷新
.write(int)
.write(byte[])
.write(byte[] , int startIndex , int len)
·
·
·

读入操作

InputStream —>FileInputStream实现类
FileInputStream fis = new FileInputStream(File/String path,boolean);
·

int .read() 每次读取下一个字节
会将读取到的字节转成对应的ASCII码返回
int .read(byte[] b) 每次读取一定数量的字节 存储在缓冲区b中
如果读入完成 没有要读入的数据 则返回-1 每次返回的是读取的字节个数

	byte[] b = new byte[2];
	int l = 0;//保存每次读取了多少个字节
	while((l=fis.read(b))!=-1) {
		System.out.println(new String(b,0,l));// new Srting ()  把读取到的东西转换为String类型
在这里插入代码片
  • 1
  • 2
  • 3
  • 4
  • 5

·
·

字节缓冲流

内部提供有默认数组形式的缓冲区 减少了IO次数

·

BufferedInputStream bis = new BufferedInputStream(FileInputStream fis);
BufferedOutputStream bos = new BufferedOutputStream(FileOutputStream fos);
  • 1
  • 2

·

字符流写出操作

Writer
FileWriter
.write(int)
.write(char[])
.write(char[],int startIndex,int len)
.write(String)
.write(String,int startIndex,int len)
现将内容读入到内存当中 通过调用.flush() .close()方法 将缓冲区内容写出
·

字符流读入操作

Reader
FileReader
·
.read() 字节流中 每次读取一个字节 字符流中 每次读取一个字符
.read(char[])

总结

写出:write()
·
字节流
一般写字符串.getBytes()–>byte[]
如果写出byte[] 需要循环写每一个元素
·
字符流
一般写字符串 字符 字符数组
BufferedWriter对象可以调用newLine() 可以在写出时表示换行效果
·
读入:read()
字节流
因为空参read方法 每次读一个字节 会导致乱码
常用缓冲区模式 byte[] b = new byte[1024]; read(b) 每次读取一个缓冲区元素个数的字节数 需要
使用循环 判断条件-1
·
字符流
read() 每次读取一个字符 不会出现乱码
当读多个字符时 可以使用缓冲区 char[] c = new char[1024]; read© 每次读取一个缓冲区元素个数的字符数 需要使用循环 判断条件-1
由BufferedReader对象可以使用readLine() 会自动判定换行 每次读取一行字符 返回String类型 需要使用循环 判断条件 null

序列化流

序列化使用类

ObjectOutputStream
1.需要对实体类进行可序列化的操作 实现implements Serializable
2.创建类的对象 同时创建序列化流对象ObjectOutputStream
FileOutputStream fos = new FileOutputStream(“d:\abc\bb.txt”);
ObjectOutputStream oos = new ObjectOutputStream(fos);
3.oos.writeObject(实例对象)
4.关闭各个流对象

反序列化使用类

ObjectInputStream
1.创建反序列化流对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(“d:\abc\bb.txt”));
2.通过读入方法 获取Object类型的对象 进行类型强转
User u = (User)ois.readObject();
System.out.println(u.toString());
3.关闭各个流对象
ois.close();

注意

如果序列化成功后 又对原类中的代码进行了修改 例如 添加或移除属性 反序列化时会报错
java.io.InvalidClassException
·
如果成员变量使用了static修饰 这个成员变量不会被序列化 不会报错 只是值没有进入字节序列 反序列化后 该成员变量为默认值
·
transient修饰成员变量 依然不会被序列化

解决:
在实体类中 添加private static final long serialVersionUID属性 设定一个值
要重新进行序列化操作
·
·

数据库

概念

数据库服务器:就是安装了数据库软件的电脑 硬件性能有一定的要求
  • 1

内存条 临时记忆 速度比较快 不能永久保存
·
硬盘 U盘 持久化数据 可以对于数据进行批量增删改查 可以永久保存

·

分类

Oracle 收费数据库 性能高 服务额外收费 比较贵
MySql 开源免费的数据库 Sun公司 Oracle收购 6.0后收费
SqlServer 微软提供的数据库 收费数据库
DB2 IBM公司提供的数据库 收费数据库 银行及政府机构
SQLite 嵌入式小型数据库 移动设备中 Android系统中

·
·
JDBC技术 实现java连接数据库

·

mySql 免安装版 安装流程

解压版
1.解压到英文路径下
2.修改配置文件 my_default.ini

basedir = …

basedir=D:\soft\mysql-5.6.44-winx64
是在安装时 能够找到数据库文件

datadir = …

datadir=D:\soft\mysql-5.6.44-winx64\data
创建数据库的地址
3.配置环境变量
;D:\soft\mysql-5.6.44-winx64\bin
4.以管理员身份运行控制台
mysqld -install MySQL --defaults-file=“路径\my-default.ini”
提示 successfully…

用管理员打开控制台
net start MySql 看是否正常启动
net stop MySql 关闭数据库
问题解决
暂停服务
管理员打开控制台 mysqld -remove
再次打开服务 查看mysql是否被移除
控制台中打开到bin cd bin
mysqld -install
net start mysql

差文件.dll 使用安装版
开启服务后进行登陆操作
登陆:
打开控制台
mysql -uroot -p 直接回车
Enter password:直接回车
mysql>
后续指令

show databases;
退出登陆:exit 或者quit
修改密码:
mysqladmin -u root -p password 回车
Enter password:输入老密码或者直接回车
New password:输入新密码
Confirm new password:再次输入新密码

数据库通用语法规则

可以单行或多行书写 以分号判定本条指令的结束
可以使用空格或缩进增强可读性
不区分大小写 建议关键字大写
单行注释 --【空格】注释内容 – default…
多行注释 /注释内容/
·

mysql专用

#【空格】注释内容
分页查询语句

语言分类

DDL (Data Definition Language)数据定义语言 操作数据库
定义数据库的对象:创建数据库 创建表 列等
create drop alter show
·
DML (Data Manipulation Language)表内数据的增删改
insert新增 delete删除 update修改
·
DQL (Data Query Language)数据的查询
select查找
·
DCL (Data Control Language) 数据控制语言 权限
Grant权限
·
DDL
操作数据库

DDL

操作数据库
1、创建
create database 数据库名;
create database if not exists 数据库名; 如果不存在就创建
create database 数据库名 character set utf8;
·
2、展示
show databases; 展示当前软件的所有数据库
show create database 数据库名; 可以查询当前数据库的字符编码集
3、修改
alter database 数据库名 character set 新的字符编码集;(建议在创建数据库时设计好)
4、删除
drop database 数据库名;
drop database if esists 数据库名; 判断 如果这个数据库存在就删除
5、使用数据库
use 数据库名; 切换至当前数据库。
select database(); 查询当前使用的数据库名。
·
·
操作表
1、创建
create table 表名(字段名 vachar(255),。。。,
primary 外键名 foreign key (被约束的列) references 主表表名(主键列名) on update casade on delete casade );
2、查询
show tables; 展示当前数据库中所有的表
desc 表名; 展示这张表中的表结构
3、修改
alter table 老表名 rename to 新表名; 修改表名
alter table 表名 add 列名 类型; 在当前表中添加一列
alter table 表名 change 老列名 新列名 类型; 修改列名
alter table 表名 modify 列名 新类型;
alter table drop 列名; 删除列
4、删除
drop table 列名; 删除表
drop table if exists 表名; 判断是否存在 存在就删除
·
DML
表内部数据的增删改操作 insert [into] delete update
1、添加数据
insert 表名 values(值1,值2,。。。);
·

注意

列名要和值一一对应
除了形式为数字的值 其他都应该加上’'或者""
如果整行添加 可以省略列名列表 注意值的数量及顺序
如果有自增列
·
2、删除数据
delete from 表名; 删除整张表
delete from 表名 where 条件;
·

注意

做删除操作一定不要忘记删除的条件 否则整表数据都被删除
如果确实要删除整表数据 同时保存此表
truncate table 表名; 机制:删除这张表 重新复制一张原表的结构 效率高
·
3、where条件语句 运算符

= > < >= <= != <> is null
&& and || or
where goodname in (‘篮球’,‘排球’,‘钢笔’,‘铅笔’)
where price>=5 and price<=20----->where price between 5 and 20;
where name like ‘’; _代表一个字符 %代表多个字符
·
4、修改数据
update 表名 set 列名 = 新值;

注意

做修改操作一定不要忘记修改的条件 否则整表每一行数据的当前列都会被改变
update 表名 set 列名 = 新值 where 条件;

将表转储为sql文件 可以获取标准语法结构的创建和数据新增语句

·
DQL
数据的查询操作 select 查询出的结果 是一张虚拟表
select * from 表名; *代表所有列
·
select 列名1,列名2,列名3… from 表名 [where 条件]; 展示满足条件的这些行里的某些列
SELECT DISTINCT shopName FROM goods; 去除当前查询列中重复的值
SELECT id,shopname, goodname,IFNULL(price+20,100),type FROM goods;

排序操作

order by 排序列名 排序规则【asc/desc】;
一般放在查询语句最后面 默认升序 也可以添加 asc 降序 desc
对多列进行排序展示 用,隔开 ASC依然可以省略
SELECT * FROM goods ORDER BY type DESC,price;
·
·

聚合函数

count(列名) 计算行数 count() 建议以某个列名代替
注意点:
聚合函数不能和普通列一起展示 可以和分组列一起展示 也可以和其他聚合函数一起使用
后面可以追加where条件
SUM(列名) 总和
MAX(列名) 最大值
MIN(列名) 最小值
AVG(列名) 平均值
·
分组查询
group by 分组列名
概念:根据分组列中的值来进行分组 拥有相同的值的行分在一组(一张虚拟表)
可以在查询时使用聚合函数 那么是对分组产生的每一张虚拟表分别进行聚合函数的执行
·
代码编写顺序: where group by order by
·
SELECT sex , COUNT(sex) AS 人数 FROM students
– 先通过where对真实表进行筛选 获取一张虚拟表
WHERE schoolname = ‘蜗牛学院’
– 依照虚拟表中的sex列进行分组 获取到两张(因为sex只有两个值)虚拟表
GROUP BY sex
– 对于两张虚拟表 分别进行聚合函数的查询
– 所以要展示的数据准备妥当 通过排序来决定展示的效果
ORDER BY 人数 DESC
·
展示蜗牛学院中 大于1人的不同年龄段的人数 并按照人数升序 年龄降序排列
SELECT age, COUNT(age) AS 人数 FROM students
WHERE schoolname = ‘蜗牛学院’
GROUP BY age
– 对于分组之后并且通过聚合函数处理过的虚拟表再次进行筛选 使用having
HAVING 人数>1
ORDER BY 人数 ,age DESC
·
做查询时 如果有以下关键词 编写顺序如下
select
from
where
group by
having
limit
limit 和 order by 不能同时操作
·
·

where 和 having

1.都是做条件筛选的
2.where是对于真实表进行筛选 获取虚拟表
3.having是对于分组之后的虚拟表进行筛选 再次获取虚拟表
4.where 后不可以跟聚合函数
5.having 后可以跟聚合函数参与条件筛选

·

约束

概念:在设计表时 通过对表中的列(字段)可录入的数据进行限定 保证将来录数据时的完整性 有效性 正确性
·
分类
主键约束–》内部包含自增列
非空约束 not null
唯一约束
外键约束
·

主键约束

添加列时设置主键:
create table 表名(
id int primary key auto_increment,
bodyId varchar(255),
name varchar(255) not null
)
– 设置多列为主键的语法
CREATE TABLE yueshu (
id int(11) NOT NULL AUTO_INCREMENT,
bodyId varchar(255) NOT NULL,
name varchar(255) NOT NULL,
PRIMARY KEY (id,bodyId)
)
主键效果:不允许重复 不能为空 非空且唯一
一般作为一张表的唯一标识 使用自增列效果
一张表可以有多个主键 称为联合主键 一般非自增列的主键都是作为其他表的外键存在的
联合主键中 只要有一个键不为空 就满足非空 只要有一个键不重复 就满足唯一
自增列只能存在于主键列
添加主键
alter table 表名 modify 列名 新类型; 修改类型
alter table 表名 modify 列名 新类型 primary key; 添加主键

删除主键
alter table 表名 drop primary key;
注意:先移除主键列的自增效果 否则删除失败   当有联合主键时 主键全部删除
  • 1
  • 2
  • 3

自增列auto_increment 只能添加在主键列 并且是数值类型上
添加自增
alter table 表名 modify 列名 新类型 auto_increment;
删除自增
alter table 表名 modify 列名 新类型;

同时设置主键和自增列
alter table 表名 modify 列名 新类型 primary key auto_increment;
  • 1
  • 2

·
·

非空约束

后期添加非空约束
alter table 表名 modify 列名 新类型 not null;
删除非空约束
alter table 表名 modify 列名 新类型;
主键必然非空

·
·

唯一约束 unique

create table 表名(
id int primary key auto_increment,
bodyId varchar(255),
name varchar(255) unique not null 设置唯一约束 且不为空
)
后期添加唯一约束
方法一:
ALTER TABLE yueshu MODIFY name VARCHAR(255) UNIQUE;
方法二:
ALTER TABLE yueshu ADD UNIQUE(name);

后期移除唯一约束 其实是移除列中用于判断是否有重复值的索引
ALTER TABLE yueshu DROP INDEX name;

注意:唯一约束的列可以为空 多个null不算重复值 可以录入
·
·

外键约束

至少涉及到两张表 表现表与表之间的关系 取值约束
主表的主键 约束住 从表的某个列的取值

如果使用软件设计外键关系 右键从 从表进入设计界面 选择外键选项卡 设置

在设计表时设计主外键关系
先创建好主表
再创建从表 这个时候设置外键约束

create table 表名(
	id int primary key auto_increment,
	bodyId varchar(255),
	name varchar(255) not null
	gender int ,
	-- 设置外键
	constraint 外键名称 foreign key(从表被约束列名) references 主表表名(主键列名)
)

后期添加主外键关系
alter table 表名 add constraint 外键名称 foreign key(被约束的列名) references 主表表名(主键列名)
后期删除主外键关系
alter table 表名 drop foreign key 外键名称
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

对于有主外键关联的表 新增和删除数据要注意:
先增主表再增从表
先删从表再删主表
修改:先增主表 再修改从表
添加级联操作:修改 删除
create table 表名(
id int primary key auto_increment,
bodyId varchar(255),
name varchar(255) not null
gender int ,

·
·

设置外键

constraint 外键名称 foreign key(从表被约束列名) references 主表表名(主键列名) on update cascade on delete cascade
)

·
格式解析
reign key(被约束的列名) references 主表表名(主键列名) on update cascade on delete cascade
添加修改级联:修改主表的主键列的值 如果从表使用了原来被改变前的值 则现在会更新为主表改变后的值
添加删除级联:删除主表的主键值 从表如果有数据行使用这个值 则这行数据整个被删除

·
·

联合查询

SELECT teachers.id,teachers.teachername,genders.gender
FROM teachers,genders
WHERE teachers.gender = genders.id

SELECT teachers.id,teachers.teachername,genders.gender,classes.classname
FROM teachers,genders,classes
WHERE teachers.gender = genders.id AND teachers.classname=classes.id AND teachers.teachername = ‘张三’

有多个表进行内连接查询 当共同被查询 没有添加任何条件时 所得到的虚拟表为每张表的行互相匹配
虚拟表的行数 = 每张表的行数的乘积 称之为 笛卡尔积
添加合适的条件 去消除无用的数据
内连接查询
隐式内连接查询 不关心表的主从 最常用
SELECT teachers.id,teachers.teachername,genders.gender ,classes.calssname
FROM teachers,genders,classes
WHERE teachers.gender = genders.id AND teachers.classname = classes.id
显式内连接查询 要区分主从表
SELECT t.id,t.teachername,g.gender,c.name
FROM teachers AS t

    [INNER] JOIN genders AS g 
    ON t.gender = g.id
    
    [INNER] JOIN classes AS c
    ON t.class = c.id
  • 1
  • 2
  • 3
  • 4
  • 5

外连接查询
左外连接
– 左外连接 teachers主
SELECT t.id,t.teachername,g.gender,c.classname
FROM teachers AS t
LEFT JOIN genders AS g
ON t.gender = g.id
LEFT JOIN classes AS c
ON t.classname = c.id
右外连接
– 右外连接 genders主
SELECT t.id,t.teachername,g.gender
FROM teachers AS t
RIGHT JOIN genders AS g
ON t.gender = g.id
·
·

子查询

概念:在查询中嵌套查询
*子查询的结果(值)作为父查询的条件
SELECT teachers.id,teachers.teachername,genders.gender,classes.classname
FROM teachers ,genders,classes
WHERE teachers.gender = (SELECT id FROM genders WHERE gender = ‘男’)
AND teachers.classname = classes.id
AND teachers.gender = genders.id ;
*子查询的结果(虚拟表) 作为父查询的查询来源
SELECT teachers.id,teachers.teachername,gen.gender,classes.classname
FROM teachers ,(SELECT * FROM genders WHERE gender = ‘男’) AS gen,classes
WHERE teachers.classname = classes.id
AND teachers.gender = gen.id ;
·
·

数据库的设计

根据需求思考表与表之间的关系 再设计表及表中的列 不同表中列和列的关系
表与表之间的结构关系
·
》》一对一
例如: 人和身份证
实现:如果出现一对一 一般把这两列存在一张表中
如果不在一张表中 就要满足 【任意】一方向另一方添加外键约束都不会造成数据存储的影响才叫一对一
没有方向性
》》一对多
例如:学生 和 性别 学生(多表) 和班级(一表)
实现:通过多的一方添加外键 指向一的一方的主键 有方向性
》》多对多
例如:学生和选修课 一个学生可以选多门课程 一个课程可以有多个学生
实现:创建一张中间表作为从表 两张多表的主键去约束从表两个列的取值 这张从表就保存两张多表中值的关系
通过三表联合查询 获取结果
·

六大范式

第一范式:1NF 每一列都是不可拆分的
第二范式:2NF 非主键列要直接依赖于主键 如果没有合适主键选择 应该创建ID列 否则应该将此列单独建表
第三范式:3NF 非主键列之间不能产生依赖关系 也不能间接依赖于主键
巴斯科德范式: 是对三范式的加强
第四范式4NF
第五范式5NF
·

事务

意义:为了完成一个目的 进行多次数据操作中 保证数据的完整性 使用事务将这一系列的sql操作包裹起来
目的:被包裹在一个事务中的sql操作 一个失败 全部失败 全部成功 才算成功(才会把数据更新)
使用:通过编码实现 判定如果事务中的所有执行都OK 则设置提交 如果有一个失败 则设置回滚
·
·
mysql数据库 属于自动提交事务操作 默认每条sql就是一个事务 执行自动提交
Oracle数据库 属于手动提交事务 如果没有提交 数据库中数据并不会更新

将mysql语句变为手动提交方式 也就是其他手动提交事务的操作方式
SELECT @@autocommit; 查询提交方式 1 自动提交 0 手动提交

start transaction;开启事务
编写多条sql语句
如果判定所有sql语句都正常执行成功 则提交 COMMIT; 真正的更新数据
如果有任意一条语句执行失败 则回滚 ROLLBACK; 使用回滚 数据会保持事务开始之前的状态

·
·

事务的四大特性

1.原子性:
定义好的一个事务 其中无论多少条sql语句都应该是不可分割的最小单位 要么同时成功 要么同时失败
2.持久性
只有当真正执行了commit 或者 rollback后 数据库中的数据才持久化保存下来 如果没有执行任意一个操作 有可能查询出错误数据
3.隔离性
事务与事务之间是相互独立的 如果操作同一数据 那么要观察当前事务的隔离级别
4.一致性
事务操作前后 数据总量是不变的 保证数据的完整性
·
·

事务的隔离级别

在这里插入图片描述脏读:读到未提交的数据
不可重复读: 多次读取 ,前后读取到的数据内容不一致
幻读: 多次读取 ,读取到的数据数量不一致
·
·
查看事务隔离级别

  • select @@tx_isolation;
    设置事务隔离级别
    set global transaction isolation level 级别名称
    重新设置完隔离级别后 需要关闭连接 重启连接 才能实现
    ·
    ·
    串行化的引用:
    先将事务隔离级别提升为SERIALIZABLE
    SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE
    然后使用第一个事务进行 增删改操作 但不提交
    开启第二个事务 进行数据库操作 会发现 第二个事务被挂起 等待执行
    只有等第一个事务提交或者回滚 结束 第二个事务才能开始执行
    ·
    ·
    悲观锁
    ****事务中的业务大概率会出现并发异常时 我们处于一种悲观状态 可以使用悲观锁
    SELECT stu.stuname FROM stu FOR UPDATE; 设置加锁的列
    UPDATE stu SET stuname = ‘bbc’ WHERE id = 16;
    先给更新操作加悲观锁 然后通过事务去执行更新 但是不提交
    第二个事务查询可以正常执行 但是更新操作会被挂起 只有等第一个事务结束 才会执行第二个事务
    ·
    ·
    乐观锁
    ****评估事务中发生并发异常的可能性比较低 但不能保证完全不会发生 可以使用乐观锁
    通过使用sql本身的语法进行校验 看是否要继续执行

      select stuname from stu where id = 16;
      update stu set stuname = '新值' where id = 16 and stuname = 刚才查出的值 
      合成为:
      update stu set stuname = '新值' where id = 16 and stuname = 
      (select t from (select stuname as t from stu where id = 16)as s);
      
      悲观锁选择列进行添加锁 当其他事务对于这个列进行操作时 会被挂起
      和串行化的区别 悲观锁中第二个事务可以正常查询 串行化中第二个事务查询也会被挂起
      乐观锁只对本行要操作的列进行限制 其他事务如果操作这个值 会被挂起 其他操作不受影响
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

·
·

##DCL
对于数据库用户权限的相关设置
use mysql;
select * from user
– 重置root管理员的密码(用于密码丢失)
1.关闭mysql服务
2.使用无验证方式登录mysql mysqld --skip-grant-tables
3.打开新的CMD 直接输入mysql 回车 登录进入mysql 显示mysql>
4.use mysql; 设定要操作的数据库
5.UPDATE USER SET PASSWORD = PASSWORD(‘333’) WHERE USER = ‘root’;
6.关闭控制台
7.重新开启服务 使用新密码登录

权限管理:
– 查询某个管理员的权限
SHOW GRANTS FOR ‘bbb’@‘localhost’;
– 列表方式赋予权限
GRANT SELECT,UPDATE,DELETE ON class49_test.stu TO ‘bbb’@‘localhost’;
– 以列表方式移除权限
REVOKE SELECT ON class49_test.stu FROM ‘bbb’@‘localhost’;
– 拥有所有权限
GRANT ALL on class49_test.* TO ‘bbb’@‘localhost’;
– 删除所有权限
REVOKE ALL ON class49_test.* FROM ‘bbb’@‘localhost’;

注意:以列表方式赋权限 就要以 列表方式移除权限
同理 以all方式赋权限 则只能以all方式移除权限

·
·
·

JDBC

	java database connectivity java连接数据库技术
	java官方为了实现访问和操作各个数据库 提供的一套标准 即接口
	由各个数据库厂家来实现这些接口 实现类都会被封装在一个jar包中
	
具体操作:
	1.导入jar包 
	将下载好的对应数据库的jar包放入lib文件夹中 build path-->add to build path
	2.代码中加载驱动
		Class.forName("com.mysql.jdbc.Driver");
	3.获取连接
    	Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/数据库名称?useUnicode=true&characterEncoding=utf-8", "root", "");
    4.先编写sql语句
    5.通过连接对象创建Statement接口的对象(该对象提供了对于数据库表进行增删改查的方法)
    PreparedStatement ps = conn.prepareStatement(sql);//优化后sql语句不放在此处
    6.调用执行方法 
    	//优化后 sql语句放入执行的方法中
    	 int executeUpdate() 增删改共用的方法 返回int
   		 ResultSet executeQuery() 查询方法 返回ResultSet结果集对象(无论查询的结果是否是多个值 都用结果集接收)
   		 针对于查询结果集 进行相关的取值操作
   		 while(rs.next()) {//判断下一个值是否存在 并跳入下一个值
			//进入一次 rs代表一行
			对应类型的变量 = rs.get值的类型(本行中该数据所在查询结果集中列的序号 从1开始);
		}
   	7.如果有结果集 先关闭结果集rs.close()
   		关闭preparedStatment对象 ps.close()
   		关闭连接 conn.close()
   		
   
   原始写法的问题:
   1.连接sql语句比较麻烦 单双引号嵌套容易出错
   2.每一次都需要重构完整的sql语句 降低了操作效率
   3.会出现sql注入异常
   	select * from 表名 where username = '值1' and password = '值2';2 输入了一个  ' or '1' = '1   
   	select * from 表名 where username = '值1' and password =2;2 输入了一个 4 or 1=1
   	本来不满足查询条件 但是依然可以查询
   	
   	采用sql语句预编译的方案 结局以上问题
   	1.先编写sql语句框架 其中一切待输入的值 都用 ? 占位 不再区分是否需要单引号
   		String sql1 = "insert students values(?,?,?,?,?,?,?)";
   	2.创建PreparedStatement 对象 将sql语句加入该对象 进行预编译
   		PreparedStatement ps = conn.prepareStatement(sql);
   	3.为sql语句中的占位符 挨个赋值
   		ps.setInt(1,);   是什么类型 就用对应的set方法 第一个参数为占位符的序列
   	4.执行对应的方法
   		增删改int excuteUpdate()   查询  ResultSet excuteQuery()
   	
   	
  再次优化
  	将增删改提取出共同的一个执行方法
  	public int iduMethod(String sql,Object[] obj) throws ClassNotFoundException, SQLException {
		int i = 0;
		//获取连接对象
		conn = getConn();
		// 创建执行对象 预编译 之后 sql并不完整
		ps = conn.prepareStatement(sql);
		
		//通过循环 将数组中的值 一个一个赋给sql语句中的? 补充完整
		for(int l = 0; l < obj.length;l++) {
			ps.setObject(l+1, obj[l]);
		}

		// 执行sql语句
		i = ps.executeUpdate();
		// 关闭数据连接相关对象
		ps.close();
		conn.close();
		return i;
	}
	
	再准备各自方法 调用以上这个共有方法
	public void addStu(){
		String sql = "insert students values(?,?,?,?,?,?,?)";
		Object[] obj = new Object[] {157,"豆豆",18,"女","5675675676","大坪","龙湖时代"};
		Util u = new Util();
		int i = u.iduMethod(sql, obj);
		System.out.println(i);
	}
   	
  
  分层结构设计开发项目
  设计和创建出数据库及各种表 录入数据
  entity包:存放实体类
  dao包:数据库连接类 接口类
  impl包:对于接口类的实现类
  util包:辅助工具类 
  test包:测试类
  
  静态页面  动态网站
  
  
  
  练习:
  	1.新增品类
  	*2.展示品类
  	*3.根据原有品类名修改品类(隐藏了一个子表的查询 提示用户信息)
  	*4.删除品类(查询一下子表中是否有当前品类商品 提醒用户)
  	
  	*1.新增商品
  	2.展示所有的商品
  	3.根据商品名称查询商品信息
  	*4.根据商品品类查询所有商品
  	5.将所有食品类的商品加价56.展示各品类商品的数量
  	7.删除某个品类下的所有商品
  	*8.按名称删除商品
  	
  	
  JDBC中去实现事务操作
  	conn.setautocommit(false);//开启事务
  	conn.commit();//提交事务catch模块中去conn.rollback();//事务回滚
  	
  	注意:事务中所有的DMLsql语句 都要关闭各种资源对象 一般放在finally
  	
  	
  使用Properties配置文件来实现相关信息的读取
  1.在src文件夹内创建properties的文件 例如:jdbc.properties 编写内容
  	url=jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf-8
    user=root
    password=
    driver=com.mysql.jdbc.Driver
  2.代码中通过反射获取这个配置文件的实例化对象
  	//读取properties文件 获取其对应的FileReader对象
	//全程只需要读取一次 
	static {
		//找到当前本类的加载器对象
		ClassLoader loader = BaseDao.class.getClassLoader();
		//从加载器对象中找到jdbc.properties配置文件的URL地址
		URL url2 = loader.getResource("jdbc.properties");
		//获取字符串形式的文件地址信息
		String path = url2.getPath();
		//  /C:/Users/woniuxy/eclipse-workspace/Class49_jdbcTest/bin/jdbc.properties
		//System.out.println(path);
		//获取其字符读入流对象
		FileReader fr = null;
		try {
			fr = new FileReader(path);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//创建一个空的properties对象
		Properties pro = new Properties();
		//将文件的字符读入流对象加载进这个空的properties对象中
		try {
			pro.load(fr);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//这样 这个pro就是jdbc.properties的实例化对象
	}
	
	
	static {
		//找到当前本类的加载器对象
		ClassLoader loader = BaseDao.class.getClassLoader();
		//从加载器对象中找到jdbc.properties配置文件的URL地址
		URL url2 = loader.getResource("jdbc.properties");
		//获取字符串形式的文件地址信息
		String path = url2.getPath();
		Properties pro = new Properties();
		try {
			pro.load(new FileReader(path));
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//这样 这个pro就是jdbc.properties的实例化对象
		//取出配置文件中各个键对应的值
		url = pro.getProperty("url");
		user = pro.getProperty("user");
		password = pro.getProperty("password");
		driver = pro.getProperty("driver");
	}
	
	
连接池技术
	1.概念:连接池就是一个容器 一个集合 内部存放多条连接对象
	2.目的:节约资源 访问效率高 控制访问量
	当系统初始化后 容器被创建 容器中会申请一些空连接 当用户访问数据库时 会从连接池中获取空连接 如果空连接已经被占用完了 会去继续创建连接 如果发现连接数已经达到最大值 那么执行等待 等待超时会报错 如果在等待过程中有连接被释放出来归还到连接池 则等待的用户可以使用这个连接
	3.实现方案:
		1》C3P0 数据库连接技术
		2》Druid 阿里巴巴提供的连接池技术 性能更优
		
C3P0:
	1.导入jar包(3个)
		c3p0-0.9.5.2.jar
		mchange-commons-java-0.2.12.jar
		mysql-connector-java-5.1.37-bin.jar
	2.修改配置文件.xml文件
		<default-config>
            <!--  连接参数 -->
            <property name="driverClass">com.mysql.jdbc.Driver</property>
            <property name="jdbcUrl">jdbc:mysql://localhost:3306/shop?characterEncoding=utf-8</property>
            <property name="user">root</property>
            <property name="password"></property>

            <!-- 连接池参数 -->
            <!--初始化申请的连接数量-->
            <property name="initialPoolSize">5</property>
            <!--最大的连接数量-->
            <property name="maxPoolSize">7</property>
            <!--超时时间-->
            <property name="checkoutTimeout">3000</property>
          </default-config>
          
          //BaseDao类中1.创建数据库连接池对象
		DataSource ds = new ComboPooledDataSource();
		public Connection getConn(){
            //2.获取连接对象
            conn = ds.getConnection();
		}
		
Druid
	//druid连接池
	public Connection getConn() {
		//字符流
		FileReader fr=null;
		fr = new FileReader(BaseDao.class.getClassLoader().getResource("druid.properties").getPath());
		//字节流
		//InputStream is = BaseDao.class.getClassLoader().getResourceAsStream("druid.properties");
		Properties pro = new Properties();
		pro.load(fr);
		//获取连接池对象
		DataSource ds = null;
		ds = DruidDataSourceFactory.createDataSource(pro);
		conn = ds.getConnection();
		return conn;
	}
	
.xml 更严谨的配置文件方案 框架自动读取 内部以成对出现的标签记录数据 使用者只需要修改文件中的值 获取连接池对象
.properties 配置文件内容以键值对出现 需要使用者编写读取代码 获取连接或者连接池对象
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238

线程

进程:正在运行的程序
线程:是进程中执行的一个小单元
关系:一个进程中至少有一个进程,一个线程一定属于某个进程。
多线程引用程序 每个线程去争夺CPU的处理时间片段
·

CPU线程调度 时间片段的分配

1.抢占式调度:优先级可以在一定程度上是线程抢到时间片段的几率提高 但不绝对 当优先级相同时 随机选择线程执行
2.时间调度:所有线程轮流获得CPU的使用权 CPU平均分配给每个CPU时间
多核处理器:
在单核的线程切换中 使切换的频率翻倍 各线程能更高效流畅的切换 各线程都运行的十分顺畅
但并没有提高程序的运行速度 只是提高了CPU的使用率

主线程:main() 从启动项目 启动虚拟机 由虚拟机去找操作系统开辟线程 CPU就多了一个要执行的线程 线程都有自己的默认名字 主线程线程名为main
守护线程:GC线程 垃圾回收线程 随着主线程的开启而开启 主线程的死亡而死亡

Thread.currentThread().getName() 用于获取当前线程的名称

·
·

继承Thread方案

例:

        double money;
        @Override
        public void run() {
            // TODO Auto-generated method stub
            //super.run();
            for(int i=0;i<100;i++) {
                money+=100;
                System.out.println(Thread.currentThread().getName()+" "+money);
            }
        }
    }


 public static void main(String[] args) throws InterruptedException {
		//展示主线程线程名:main
		//System.out.println(Thread.currentThread().getName());
		
		//自创建线程默认名:Thread-0 Thread-1...
		Bank bank1 = new Bank();
		bank1.money=200;
		//bank1.addMoney();
		//bank1.start();
		//创建线程对象 将之前线程放入 额外可以放入String类型 作为线程名称 
		Thread thread1 = new Thread(bank1,"张三");
		//调整线程优先级 可以提高线程获取到CPU时间片段的几率
		//默认优先级为5  Thread.NORM_PRIORITY
		//最高优先级 10  Thread.MAX_PRIORITY
		//最低优先级 1	Thread.MIN_PRIORITY
		thread1.setPriority(Thread.MIN_PRIORITY);//1
		//开启线程
		thread1.start();
		
		Bank bank2 = new Bank();
		bank2.money=1000;
		bank2.name="bbb";
		//bank2.addMoney();
		//bank2.start();
		Thread thread2 = new Thread(bank2,"李四");
		thread2.setPriority(Thread.MAX_PRIORITY);//10
		thread2.start();
		
		//线程休眠 一定时间(毫秒)以后自动执行
		Thread.sleep(3000);
	}
	
在这里插入代码片
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

注意

如果调用run() 则只是普通的方法调用 只有使用start()才是启动线程
线程停止:.stop() .resume() .suspend() 以上三个方法都已被弃用
建议使用循环判断结束 来结束线程任务
·
·

实现Runnable接口

注意:
Runnable接口是一个标准的函数式接口 因此可以使用lambda表达式
(除此之外 还有外部比较器也是标准函数式接口)
new Thread(()->{System.out.println(“现成方法”);},“线程”).start();
·
·

实现Callable<接口> 实现E call();

执行时 采用线程池提供线程 绑定任务
		//创建线程池 规定内部有几个线程 再获取线程池的对象
		ExecutorService pool = Executors.newFixedThreadPool(2);
		
		//获取线程中要完成的任务
		Bank b1 = new Bank(5,4);
		Bank b2 = new Bank(4,4);
		
		//取出线程 和任务进行绑定 
		//任务获取线程的顺序 依然是代码顺序
		//如果都获取到线程后 线程来进行CPU时间片段的争抢
		Future<Integer> future1 = pool.submit(b1);
		Future<Integer> future2 = pool.submit(b2);
		
		//可以通过线程对象.get() 获取其返回值
		//System.out.println(future1.get());
		//System.out.println(future2.get());
		
		//关闭线程
		pool.shutdown();
		
		
//不确定线程数量的线程池
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

总结

Thread:继承类的对象 就是线程
例如 Bank extends Thread … new Bank.start() 对象本身就可以进行执行

Runnable:实现类的对象 并不是线程 而是任务 如果测试中没有或得到线程 则不能按照线程来执行
	例如 Bank implements Runnable...    new Thread(new Bank(),"线程名称").start()
	
Callable:实现类的对象并不是线程 而是任务 如果测试中没有通过线程池获取线程 则不能按照线程来执行
	例如 Bank implements Callable... 
	Future<返回类型> future = Executors.newFixedThreadPool(2).submit(new Bank());
	future.get();//任务的返回值

如果多线程操作(修改)同一数据对象 应使用Runnable、Callable 不能使用Thread 
(继承自Thread 本身就是一个线程 那么想要操作同一个数据 还是需要像使用Runnable接口一样 去创建多个线程 将同一个子类对象 放入其中)
	Bank bank = new Bank();
	Thread t1 = new Thread(bank,"甲");
	Thread t2 = new Thread(bank,"乙");
	t1.start();
	t2.start();
(但是Thread本身是一个线程 自己占用一个线程 直接.start()就不能实现操作同一个数据)
	Bank bank1 = new Bank();
	Bank bank2 = new Bank();
	bank1.start();
	bank2.start();

(实现自Runnable接口的类 只能通过创建多个线程对象 去开启执行 也就是 任务和线程绑定执行)
	Bank bank = new Bank();
	Thread t1 = new Thread(bank,"甲");
	Thread t2 = new Thread(bank,"乙");		
	t1.start();
	t2.start();

使用接口更适合于资源的共享
接口可以规避java的单继承约束
代码(任务)和线程相对独立 代码(任务)可以被多个线程共享
线程池只能放入实现Runnable或Callable的类的线程 不能放继承自Thread的类
·
·
·
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

线程安全问题

多个线程 操作同一个数据 并且修改数据 大概率出现数据异常问题 就是要解决的线程安全问题
问题来源:数据在内存中修改的速度慢于线程切换的速度
解决方案:给线程加锁 在同一时刻 只能有一个线程对于数据进行操作 只有等这个线程将任务执行完毕 也就是修改的数据被完全保存 其他线程才又获得争抢CPU时间片段的机会

同步代码块三种解决方案:
	1.同步代码块
		习惯性外部一般是循环(但非必须) 1.整个线程是多个循环步骤构成2.结束线程
		while(){
            synchronized(锁对象){
                某个抢夺到CPU使用权限的线程 本次要执行的任务
            }
		}
	2.同步方法
    	public synchronized 返回值类型 方法名(){
    		方法体为被约束的内容 同时只能有一个线程去执行 
    	}
    	这个同步方法 最终被run()方法调用
    3.方法中的同步代码块
    	同步代码块可以放在一个普通方法中 在这个方法中 被同步代码块包裹的代码同时只能有一个线程执行 其他线程等待下一次抢夺CPU时间片段的机会 这个同步代码块以外的代码 所有线程都可以争抢执行·
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

·
·
·

死锁

你先给我锁 我执行完再给你
死锁产生的条件:
1》互斥条件 一个资源每次只能被一个线程使用
2》请求与保持的条件 在一个线程未完成前 不会释放自己已有的资源
3》不剥夺条件 线程获取到的资源 在线程未完成前 不能强行剥夺
4》循环等待条件 若干线程间形成一种头尾相连的循环等待资源关系
避免死锁:
不要让线程在执行过程中 持续保留一个重要的资源
在线程等待过程中 永久占用系统资源

线程与线程之间 拥有竞争关系 互相争夺CPU的时间片段 争夺线程任务中的锁对象
同时线程与线程之间 还有协作通讯的关系

线程关键方法:
	1》sleep(long) 线程休眠 但是线程并未中断
	2》isInterrupted() 判断某个线程是否是中断了  是 true  否 false
		interrupt() 强制某个线程中断
	3》wait() 无限等待 后续线程可以强用CPU执行 直到这个线程被唤醒
	4》notify()唤醒等待的线程 
		notifyAll()唤醒所有等待的线程
	等待的线程和唤醒线程 需要使用同一把对象锁 是对于对象锁的等待和唤醒
	5》yield()礼让 告诉虚拟机 当前线程可以礼让 但是是否真正礼让成功 未知 由CPU分配时间片段
	6》join()强制执行 强制执行的线程.join()
	·
	·
	  打断interrupt()
	更关注的是是否处于打断的状态 并不会使当前线程真正停止或者降低获取CPU时间的几率
**sleep() wait()区别
1.sleep()来自于Thread类 wait()来自于Object类
2.任何线程都可以使用sleep()是使用线程对象 Thread.sleep(时间)  但是想要使用wait()及notify() notifyAll()该线程必须有对象锁 是使用锁去调用这些方法
3.sleep()并不会释放锁 wait()会主动释放锁 
	sleep()时间一到 自然醒 wait()需要使用同一把锁去唤醒  唤醒后 都是继续向下执行
4.sleep()需要抛出可能被打断的异常 wait()不需要 只需要另一个线程使用同一把锁唤醒 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

·
·
·

·

线程池

在这里	默认线程数量线程池
	ExecutorService pool = Executors.newFixedThreadPool(2);
	pool.submit(线程对象);
	pool.shutdown();
	动态线程池
	ExecutorService es = Executors.newCachedThreadPool();
	es.execute(run);
	es.shutdown();插入代码片
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

型号量

Semaphore 通过构造方法传入int类型的参数 表示同时可以有几个线程执行
	void  .acquire()  
	可以判定当前任务中的线程数量是否已经达到最大值 若达到则屏蔽其他线程 若没达到 则放行
	void  .release() 
	进行信号量对象中线程数量的更新
	
	//设置信号量
	Semaphore se = new Semaphore(2);
	//线程任务中判定当前进入任务的线程数量
	se.acquire();
	//每个任务结束后 要更新信号量
	se.release();

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

同步屏障

Semaphore 通过构造方法传入int类型的参数 表示同时可以有几个线程执行
	void  .acquire()  
	可以判定当前任务中的线程数量是否已经达到最大值 若达到则屏蔽其他线程 若没达到 则放行
	void  .release() 
	进行信号量对象中线程数量的更新
	
	//设置信号量
	Semaphore se = new Semaphore(2);
	//线程任务中判定当前进入任务的线程数量
	se.acquire();
	//每个任务结束后 要更新信号量
	se.release();

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

·
·

网络编程

无论B/S项目 还是C/S项目 都一定需要网络进行数据的传递
网络编程基础
概念:在一定的协议之下 实现多台电脑之间的通信
网络通信协议:
网络编程及网络信息互通依赖的基础
TCP/IP协议:四层结构:链路层 网络层 传输层 应用层
有连接对象的 通讯协议安全性高 定义计算机如何连入互联网 及数据如何传输
UDP协议:
无连接通信协议 数据发送端和接收端不建立逻辑连接 安全性低 有可能导致信息缺失 但是占用网络资源少 效率高 一般用于音频 视频会议等

TCP/IP协议三次握手建立逻辑连接 保证客户端与服务器端连接的完整 数据的安全
第一次握手:客户端向服务器端发送请求 等到服务器的响应
第二次握手:服务器端回写信息给客户端 通知客户端已经接受到请求
第三次握手:客户端再次向服务器端发送请求连接
以上三次握手都成功 才真正建立起连接 进行数据的传递

服务器端:定义端口(由操作系统随机分配一个端口号 或者 软件向系统申请一个指定的端口号) 开启服务后 服务器端一直监听这个端口
客户端:向指定的端口发送请求 生成一个IO字节流对象 与服务器进行数据传输 
服务器端:当监听的端口有接收到请求时 会使用这个发送请求的客户端使用的IO流对象进行处理

网络编程三要素
协议:
IP地址:
	IPv4:一个32位的二进制数 通常被分为四个字节 xxx.xxx.xx.xxx其中每个字节都是0-255之间的十进制整数 组合排列后可以表示42亿个标识(网络地址)例如 127.0.0.1
	IPv6:IP地址新的定义规则 采用128位地址长度 每16个字节一组 分成8组十六进制数 AB89:fd23:AB23:AB23:AB89:fd23:AB23:AB23 足够所有的电脑定位使用
端口号:使用IP地址确定哪台电脑 使用端口号来确定哪个进程(服务)
综合例如:http://192.168.1.200:3306
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

网络编程:
编写服务器端

编写客户端
  • 1
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/969758
推荐阅读
相关标签
  

闽ICP备14008679号