赞
踩
每个方法都有默认的Object构造方法
但是只要重写它的构造方法后 默认的构造方法将失效
·
·
·
可以带参
*优点 创建对象的同时类中的属性进行赋值
可以不带参数
*优点 只是想调用里面的方法的话 就不加参数 方便创建对象
*new一个方法的时候就是在调用它的构造方法
·
·
·
重载
*在同一类中 方法名相同 参数个数不同 或者参数类型不同 或者 参数顺序不同
*重写
一般在子父类继承关系中 出现 或者实现接口的时候会写方法
·
·
·
方法可以带返回值 可以不带返回值 可以带参数(带参的叫形式参数) 可以不带参数
·
·
·
什么是类
*具备相属性和方法的一类对象的集合
什么是对象
*真实存在的实体
之间的关系
*类是对象的抽象话,对象是类的实例化
由对象去编写类,再由类去new对象
·
·
全局变量
*类下面的叫全局 可以不赋初始值 系统会有一个默认的初始值 (0或null);
局部变量
*方法下面的叫局部 创建时必须赋值 不然会报错
·
·
·
概念
*描述类与类之间的关系 被抽取的方法和属性的类就是子类 新生的类就是父类
结果
*子类具备父类的方法和属性
*子类必须全部重新父类的方法
关键词
*extends
特点
*满足 is—a的关系 (例如 铅笔是文具);
·
·
缺点
*java当中只能单继承 (一个子类只能有一个父类 一个父类可以有多个子类)
·
·
·
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修饰的类的成员变量和成员方法 只能被子类访问 其他类访问不到 属于受保护的
*什么修饰符都不写 在不同的包下不能访问 在相同的包下可以访问
·
·
设计接口
*关键字 设计时用 interface 实现类用 implements去实现接口
*满足 has–a 的关系(例如 usb接口有鼠标)
·
优点
*可以以间接的形式实现多继承
.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[])
.append(下标); 在下标后添加字符串
.deleteCharAt(下标); 删除下标的元素
.delete(起始下标, 结束下标+1);
.replace(起始下标, 结束下标+1, String );
.insert(插入下标, Object);
.reverse() 将内部字符重新排序 逆序
.toString() 转换为String类型
线程安全的 效率低
·
线程非安全的 效率高
*底层默认16个字符的数组 char[]
*如果初始值超出了默认长度 再+16的char数组 保存值
如果修改时 值超出了长度 则进行数组扩容 创建新的数组 长度为原长度2+2 老数组的值赋给新
数组 指针指向新数组
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 方式一:
double d = 5.578;
BigDecimal bd = new BigDecimal(Object); //会出现无尽小数
//创建方式二 推荐方式
BigDecimal bb = BigDecimal.valueOf(double\long);
·
·
DecimalFormat df = new DecimalFormat(“0.0000”);
例 System.out.println(df.format(6.45678)); //最终返回的是字符串 四舍五入 不够会补零
·
·
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 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(下标)
·
·
·
·
框架:整合的工具
集合:数组 数列 管理和操作批量数据的方案 提供了多种多样的方法
长度固定 同类型的数据
不限定长度 可以真正的保存不同类型 保存的都是对象 包装类
.add(对象) 添加对象
.addAll(对象集合)相当于复制集合
.clear()清除所有对象
.contains(对象) 判断是否包含这个对象
isEmpty() 是否有对象
Iterator() 返回当前集合的迭代器对象
remove(对象) 删除某个对象
size()集合元素的个数
toArray() 将集合转换为一个数组
set(int index,对象)替换掉对象
·
·
线程不安全的 效率高 查询快 增删慢
·
底层结构
*底层是一个数组结构
*使用带int类型的参数的构造方法 参数为自定义的长度
·
*如果大于0 则扩容其1.5倍
集合保存的最大值 MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
如果超出 则报 OutOfMemoryError错误
·
·
*特点:因为在增删过程中 会新建新数组 导致老数组称为垃圾内存
建议在新建时 预估集合大小
·
·
Itorator ii = 集合名.iterator();
有些集合没有索引 不能使用普通 for 遍历
Iterator 和 增强for循环可以遍历所有的集合
·
·
LIstIterator ii = 集合名.ListIterator(al.size);//让指针调到最后
while(ii.hasprevious()){
int i = li.previous();
System.out.print(i+"\t");
}
特点:双链结构 增删效率高 查询效率低
与数组区别:不需要频繁的进行空间的开辟 数组可以直接通过索引找到对应的元素 而链表从头开始一个一个查找 LinkedList集合没有默认长度 也没有扩容的方法
·
·
add(int index,对象)
addFirst(对象)
addLast(对象)
getFirst()
getLast()
removeFirst()
removeLast()
pop() 将第一个元素移除 返回这个元素
push(对象) 将一个元素添加到头部
offer(对象) 将对象添加到尾部
offerFirst(对象) 将对象添加到头部
offerLast(对象) 将对象添加到尾部
poll() 删除头元素
pollFirst() 删除头元素
pollLast() 删除尾元素
·
·
·
基本数据类型 和String类型 不需要去重写比较规则
比较器方案一:
直接在待比较的实体类中添加比较规则
1.实体类实现接口
public class User implements Comparable//跟上泛型类型
2.重写compareTo方法 再编写比较规则
3.使用比较器
数组 Arrays.sort(数组名) 对于数组只能重写 COmparetor的方法 基本类型的时候要用包装类
集合 Collections.sort(集合名,)
·
·
·
*无索引 无序 不能储存重复的数据
*对于基本数据类型 和 String类型 因为都重写了equals()和 hasCode()方法 所以可以实现制动去重。
*对于自定义类型 同时 存储方式为HashSet、LinkedHashSet 我们如果需要只判断值 name就需要在自定义类中去重写这两个方法 equals() hashCode()方法。
*对于TreeSet 存储任何类型都会自动去重 但是需要添加比较器 否则报错。
·
·
展示顺序
*默认情况下 储存顺序与展示顺序一致
*可以通过添加比较器 在通过Collection.sort() 实现元素的出现排序
·
·
默认情况下 存储顺序和展示顺序不一致
HashSet\LinkedHashSet
不能通过添加比较器 固定元素顺序
除非将其转成其他可以排序的集合
(转换语法 ArrayList au = new ArrayList(HashSet集合名) )
基本数据类型和String类型 都是自动去重
自定义类型
HashSet\LinkedHashSet 需要重写equals()和hashCode() 否则不能去重
TreeSet 自动去重 (注意要添加比较器 不然保存失败)
*对于基本数据类型和String类型 默认升序
*对于自定义类型 需要添加比较器 (否则保存时报错ClassCastException) 按照比较器规则排序
*不需要通过Collections
*如果实现Comparable直接迭代展示即可
*如果实现Comparator需要在创建集合时 将比较器对象作为构造方法的参数传入
·
·
·
*该接口下的集合有两个泛型 第一个泛型确定键的类型 第二个泛型确定值的类型
*是以键值对的映射的形式来保存数据
*键类似于索引 不能重复 值可以重复
·
·
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.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); } 在这里插入代码片
概念: 在程序中使用{} 定义起来的一段程序
分类
普通代码块:{} 里面声明的变量不能再外部使用
构造代码块:{}写在实体类中任意位置 优先于构造方法 每new一次 执行一次
静态代码块:{}写在普通类中 优先与构造代码块 程序运行过程中 只在开始运行一次
同步代码块:线程时会遇到
程序必须先有main() 否则运行报错
执行主类静态代码块 只执行这一次
再回到main()依次执行
当new对象的时候 :
执行该类中的静态代码块 只执行这一次
执行构造代码块
再执行构造方法
概念 : 可以用于全局属性和全局方法的声明 可以避免对象实例化的限制
*由static修饰的全局属性 被该类所有的对象共享 这样的属性保存在全局数据区中(方法区保存的元数据)
特点:
*由static修饰的成员变量和成员方法 可以由类的对象访问 也可以由类名直接访问
!!!(接口中的静态方法只能被接口类名访问 不能被接口实现类对象访问)
·
·
·
*由static修饰的方法中不能有this关键字
*由static修饰的方法中 可以调用有static修饰的方法和属性
*可以调用普通方法和普通变量
*如果要在这个方法中调用普通方法和普通全局变量 则需要先new该类的对象 对象去调用
*由非static修饰的方法中 可以调用由static修饰的方法和属性
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.创建一个类 继承一个异常的类型 继承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()); }
<E>
·
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);
·
·
·
*在一个类的内部 直接定义了另外一个类 作为成员类
*在一个类的内部的成员方法中 定义一个类 方法内的局部内部类
class Classes{
String classname;
public void show(){
int score;
class Teacher{
String teacher;
}
}
class Student{
String name;
}
}
*成员内部类中的方法中 可以通过this调用该内部类的属性和方法
可以通过外部类类名.this.外部类的属性和方法
//一:成员内部类 外部类: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();
* 通常 需要创建实现类去实现接口的方法 最终也是通过实现类的对象去执行重写后的方法
//使用匿名内部类来实现接口的方法 省略了实现类的创建 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.接口作为方法的返回值 需要在设计这个方法时 在返回之前去实现接口 获取接口的实现类对象作为返回值
抽象方法带参数 先调用外部方法获取到接口实现类对象 再调用抽象方法的时候传入参数
抽象方法带返回值 先调用外部方法获取到接口实现类对象 再调用抽象方法按照有返回值的方案操作
·
·
·
.length() 返回long 文件中内容的大小 /1024 才是kb
.exists() 返回boolean 判断当前路径文件是否存在
.createNewFile() 返回boolean 创建文件
.mkdir() 返回boolean 创建一个文件夹
.mkdirs() 返回boolean 创建多级文件目录
.isDirectory() 判断是否是一个文件夹
.isFile() 判断是否是一个文件
.delete() 删除
.listFiles() 返回一个数组保存某个文件夹下所有的文件信息 File[]
.list() 返回文件名 String[]
·
·
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类型
在这里插入代码片
·
·
内部提供有默认数组形式的缓冲区 减少了IO次数
·
BufferedInputStream bis = new BufferedInputStream(FileInputStream fis);
BufferedOutputStream bos = new BufferedOutputStream(FileOutputStream fos);
·
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属性 设定一个值
要重新进行序列化操作
·
·
数据库服务器:就是安装了数据库软件的电脑 硬件性能有一定的要求
内存条 临时记忆 速度比较快 不能永久保存
·
硬盘 U盘 持久化数据 可以对于数据进行批量增删改查 可以永久保存
·
Oracle 收费数据库 性能高 服务额外收费 比较贵
MySql 开源免费的数据库 Sun公司 Oracle收购 6.0后收费
SqlServer 微软提供的数据库 收费数据库
DB2 IBM公司提供的数据库 收费数据库 银行及政府机构
SQLite 嵌入式小型数据库 移动设备中 Android系统中
·
·
JDBC技术 实现java连接数据库
·
解压版
1.解压到英文路径下
2.修改配置文件 my_default.ini
basedir=D:\soft\mysql-5.6.44-winx64
是在安装时 能够找到数据库文件
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…
多行注释 /注释内容/
·
#【空格】注释内容
分页查询语句
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
操作数据库
操作数据库
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 不能同时操作
·
·
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;
注意:先移除主键列的自增效果 否则删除失败 当有联合主键时 主键全部删除
自增列auto_increment 只能添加在主键列 并且是数值类型上
添加自增
alter table 表名 modify 列名 新类型 auto_increment;
删除自增
alter table 表名 modify 列名 新类型;
同时设置主键和自增列
alter table 表名 modify 列名 新类型 primary key auto_increment;
·
·
后期添加非空约束
alter table 表名 modify 列名 新类型 not null;
删除非空约束
alter table 表名 modify 列名 新类型;
主键必然非空
·
·
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 外键名称
对于有主外键关联的表 新增和删除数据要注意:
先增主表再增从表
先删从表再删主表
修改:先增主表 再修改从表
添加级联操作:修改 删除
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
外连接查询
左外连接
– 左外连接 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);
悲观锁选择列进行添加锁 当其他事务对于这个列进行操作时 会被挂起
和串行化的区别 悲观锁中第二个事务可以正常查询 串行化中第二个事务查询也会被挂起
乐观锁只对本行要操作的列进行限制 其他事务如果操作这个值 会被挂起 其他操作不受影响
·
·
##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方式移除权限
·
·
·
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.将所有食品类的商品加价5元 6.展示各品类商品的数量 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 配置文件内容以键值对出现 需要使用者编写读取代码 获取连接或者连接池对象
进程:正在运行的程序
线程:是进程中执行的一个小单元
关系:一个进程中至少有一个进程,一个线程一定属于某个进程。
多线程引用程序 每个线程去争夺CPU的处理时间片段
·
1.抢占式调度:优先级可以在一定程度上是线程抢到时间片段的几率提高 但不绝对 当优先级相同时 随机选择线程执行
2.时间调度:所有线程轮流获得CPU的使用权 CPU平均分配给每个CPU时间
多核处理器:
在单核的线程切换中 使切换的频率翻倍 各线程能更高效流畅的切换 各线程都运行的十分顺畅
但并没有提高程序的运行速度 只是提高了CPU的使用率
主线程:main() 从启动项目 启动虚拟机 由虚拟机去找操作系统开辟线程 CPU就多了一个要执行的线程 线程都有自己的默认名字 主线程线程名为main
守护线程:GC线程 垃圾回收线程 随着主线程的开启而开启 主线程的死亡而死亡
Thread.currentThread().getName() 用于获取当前线程的名称
·
·
例:
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); } 在这里插入代码片
如果调用run() 则只是普通的方法调用 只有使用start()才是启动线程
线程停止:.stop() .resume() .suspend() 以上三个方法都已被弃用
建议使用循环判断结束 来结束线程任务
·
·
注意:
Runnable接口是一个标准的函数式接口 因此可以使用lambda表达式
(除此之外 还有外部比较器也是标准函数式接口)
new Thread(()->{System.out.println(“现成方法”);},“线程”).start();
·
·
执行时 采用线程池提供线程 绑定任务 //创建线程池 规定内部有几个线程 再获取线程池的对象 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(); //不确定线程数量的线程池
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的类 · · ·
多个线程 操作同一个数据 并且修改数据 大概率出现数据异常问题 就是要解决的线程安全问题
问题来源:数据在内存中修改的速度慢于线程切换的速度
解决方案:给线程加锁 在同一时刻 只能有一个线程对于数据进行操作 只有等这个线程将任务执行完毕 也就是修改的数据被完全保存 其他线程才又获得争抢CPU时间片段的机会
同步代码块三种解决方案:
1.同步代码块
习惯性外部一般是循环(但非必须) 1.整个线程是多个循环步骤构成2.结束线程
while(){
synchronized(锁对象){
某个抢夺到CPU使用权限的线程 本次要执行的任务
}
}
2.同步方法
public synchronized 返回值类型 方法名(){
方法体为被约束的内容 同时只能有一个线程去执行
}
这个同步方法 最终被run()方法调用
3.方法中的同步代码块
同步代码块可以放在一个普通方法中 在这个方法中 被同步代码块包裹的代码同时只能有一个线程执行 其他线程等待下一次抢夺CPU时间片段的机会 这个同步代码块以外的代码 所有线程都可以争抢执行·
·
·
·
你先给我锁 我执行完再给你
死锁产生的条件:
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()不需要 只需要另一个线程使用同一把锁唤醒
·
·
·
·
在这里 默认线程数量线程池
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.submit(线程对象);
pool.shutdown();
动态线程池
ExecutorService es = Executors.newCachedThreadPool();
es.execute(run);
es.shutdown();插入代码片
Semaphore 通过构造方法传入int类型的参数 表示同时可以有几个线程执行
void .acquire()
可以判定当前任务中的线程数量是否已经达到最大值 若达到则屏蔽其他线程 若没达到 则放行
void .release()
进行信号量对象中线程数量的更新
//设置信号量
Semaphore se = new Semaphore(2);
//线程任务中判定当前进入任务的线程数量
se.acquire();
//每个任务结束后 要更新信号量
se.release();
Semaphore 通过构造方法传入int类型的参数 表示同时可以有几个线程执行
void .acquire()
可以判定当前任务中的线程数量是否已经达到最大值 若达到则屏蔽其他线程 若没达到 则放行
void .release()
进行信号量对象中线程数量的更新
//设置信号量
Semaphore se = new Semaphore(2);
//线程任务中判定当前进入任务的线程数量
se.acquire();
//每个任务结束后 要更新信号量
se.release();
·
·
无论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
网络编程:
编写服务器端
编写客户端
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。