java基础
由SUN公司推出 后期被Oracle公司收购
- 语言平台javaSE:是一套标准 java的基础部分包含构成java的核心的类 数据库连接 接口 输入\输出。。。javaEE:包含javaSE 包含有企业及开发所必须的各种服务 JSP Servlet。。javaME:包含javaSE部分 移动端程序的开发
- 语言特点易于理解和学习面向对象可移植性(虚拟机)健壮性支持多线程编程安全
- 可移植性程序人员【编写】代码(xxx.java) 存放入src文件夹中
虚拟机 java语言提供了针对于各种系统(平台)的虚拟机
【编译】成(xxx.class)字节码文件 存放入bin文件夹中
【执行】执行的是.class文件
JDK(java开发工具包)《—-JRE(java运行环境)《—— JVM(虚拟机) - JDK安装步骤(解压版)解压文件 放置在合适的目录下(不要有中文名称)进入到bin文件内 创建一个java文件 注意点: 严格区分大小写 标点符号一定使用英文的 大括号小括号一定成对儿出现 类名 class(类)一定要和文件名一致
打开控制台 找到当前文件路径 使用javac xxx.java 对其进行编译(会生成一个xxx.clsss文件)使用java xxx 去执行这个字节码文件
配置环境变量1.计算机右键 属性 高级系统设置 环境变量 2.在系统变量中新建名称为JAVA_HOME的变量 3.将JDK的路径名放入其中 4.找到系统变量中的Path 点击编辑 5.在变量值的最后面添加;%JAVA_HOME%\bin 验证: 打开控制台 直接输入javac java java【空格】-version
控制台操作指令: c: d: 打开某个盘 cd 文件夹名称 打开文件夹 cd.. 返回上一层及 cd/ 回到根目录 dir 展示当前文件夹下所有文件 cls 清屏
操作: 新建一个test.java文件 public class test { public static void main(String[] args){ System.out.println(“Hello World”); } } 打开控制台 找到当前文件 javac test.java java test
BS:使用浏览器访问 相关业务服务 只需要浏览器 输入对应的网址信息 服务器端
CS:具备有客户端的应用程序 需要下载安装客户端 客户端 服务器端
- 注释:单行注释 //注释的内容多行注释 /*
- 关键字:一些有特殊含义的字符 String class static private..
- 标识符:起名规范: 包含字母(大小写)数字 某些特殊符号_ $ 数字和特殊符号不能开头 不能有空格类名规范: 首字母大写 后面每个单词的首字母大写 WoniuSchool方法名规范: 首字母小写 后面每个单词首字母大写 showName()变量名: 首字母小写 后面每个单词首字母大写 nameStudent 包名:主要描述项目 通过.构建层级关系 bin目录和src目录是一致的注意:名称尽量有意义 不要aaa bbb
项目导入:into Workspace项目导出:File System
String nameStudent = “张三”; int ageSchool = 5;
Java基础二
程序:为了实现一个目的 编写的一系列指令的集合 按照一定的顺序去执行这些指令 :数据的游戏规则 数据的保存: 数据库保存数据 持久化数据存储方式 硬盘设备 内存存储临时使用的数据信息 内存当中 二进制存储机制 只有0 和1 两个数字9 10 11 。。。19 20 99 1000 1 10 11 100
二进制中 每一个数字叫做一个位 (比特位)bit b
字节 Byte B = 8bit
1KB = 1024B
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB
…
真正存储数据 使用以上单位 不是很合适(除了Byte)
你去沙漠中旅行 大的旅行包 太阳镜 水 卤菜 衣物 应该根据数据的大小特点来选择合适的容器
八种基本数据类型
四大类 八种 字节数 bit 数据的表示范围整型 》byte(字节) 1 8 -128 —- 127 》short(短整型) 2 16 -32768—— 32767 》int(整型) 4 32 通常使用的类型 》long(长整型) 8 64总结 保存整数通常使用int 如果超出int 就是用long
浮点型 》float(单精度浮点型)4 32 》double(双精度浮点型)8 64总结 通常使用double 来保存数据 long num2 = 123L; float num3 = 3.14F; 字符型 》char 2 16布尔类型 》boolean 1 8 char sex = ‘男’; //sex = ‘男女’; 报错 sex = ‘b’; boolean bool = true; bool = false;总结 char类型要加’’ boolean类型 只能存 true(真) false(假)注意: java中 整数默认是int类型 小数默认是double类型
String 字符串类型 String name = “王五”; 不属于八大基本数据类型
实现: 声明类型 变量名 赋值号= 值 ;变量:值可以修改变化的量常量:值不可以修改的量 数据常量 自定义常量 //数据常量 //“李四” 123
//自定义常量
final int num = 8;
//num = 9; //给常量重新赋值会报错
类型转换:(同种数据类型) 自动类型转换 范围小的数据类型转换为范围大的数据类型 long num2 = 123; double num3 = 123;—>123.0 运行过程中 有小类型和大类型 结果会自动升级为大的类型 int num = 3; double num4 = 5; double result = num+num4;—->8.0 String num4Str = num4+””;—->num4保存为字符串 强制类型转换 范围大的数据类型转换为范围小的数据类型 //强制类型转换 int num4int = (int)num4;//——->3 运算符: 算术运算符:
+ - * / %(求模运算) ++ —
int i = 4;
i++; ——> i = i + 1; ——> i += 1;
注意: 先自加 后自加
赋值运算符:
= += -=
i += 5; ——-> i = i + 5;
蜗牛商城系统商品名称 商品数量 商品单价(元)教材 5(本) 30.0笔 20(支) 5.0电脑 9(台) 5000.0总价:xxx元 其中电脑占比:xx%
比较运算符:
> < == >= <= != 最终返回的应该是一个boolean值
因此 一旦某处需要放一个boolean类型的值
1.就可以放一个包含比较运算符的表达式 2.true/false 3.布尔类型的变量
三元比较运算符:
变量 = 判断表达式 ? 值1 : 值2 ;
逻辑运算符: 最终返回的应该是一个boolean值
&&并且 两个条件都为真 才为真 一个为假 就是假
(短路与) 第一个条件为假 不会再执行判断第二个条件
||或者 两个条件为假 才是假 一个为真 就是真
(短路或) 第一个条件为真 不会再执行判断第二个条件
!取反
& 即使第一个条件为假 依然会去执行判断第二个条件
| 即使第一个条件为真 依然会去执行判断第二个条件
接收用户输入 //导包 指出当前类在哪里 类的外面 import java.util.Scanner;
main()中
// TODO Auto-generated method stub
// 接收用户输入
// 1.创建接收方法所在类的对象
// 2.需要进行导包
// 3.去运用方法接收数据
Scanner input = new Scanner(System.in);
System.out.println(“请输入用户名:”);
// 通过Scanner类提供的方法 抓取控制台输入的数据 并且保存在声明好的变量中
//String temp = input.next();
String temp = input.nextLine();
System.out.println(“请输入年龄”);
int age = input.nextInt();
System.out.println(“姓名:”+temp+”,年龄:”+age+”.”);
练习: 接收用户输入的两个数字 返回大的数字 接收用户输入的数字 判断其是否是偶数
选择和循环
程序的执行顺序: 顺序执行:每一段代码都会执行 选择执行:只有满足条件的代码才会执行 (boolean) 1.找条件 2.确定满足条件执行的代码 和不满足条件执行的代码 语法: if(判断条件){ 当满足这个条件时执行的代码 } 如果输入的是1 拿到苹果 2 拿到梨子 3 拿到哈密瓜 4 拿到芒果
特点:在同一个选择语句中 执行了其中一个代码块 其他代码块不再执行 如果判断都不成立 则执行else{}当中的内容
if(num==1) {
System.out.println(“你获得了苹果”);
}else if(num==2) {
System.out.println(“你获得了梨子”);
}else if(num==3) {
System.out.println(“你获得了哈密瓜”);
}else {
System.out.println(“你永远也吃不到苹果了”);
}
?判断条件是否需要顺序
答:如果是做区间判断 那么要注意判断条件的顺序性 要把最难满足的条件放在最上面
?什么时候应该用一个独立的if语句 什么时候 应该多几个if语句
答:判断的内容是不是同一个概念
注意点:
if 一定有一个 else if可有可无可有多个 else可有可无只能有一个
如果以上三组代码都存在的话 必须以if开头 中间放else if 最后跟上else
练习:录入小明的数学成绩 如果大于95 奖励电脑 如果大于80 奖励本子 如果大于60 奖励作业 如果小于60分 先挨打 再做作业练习:录入数学成绩和语文成绩 数学成绩>60 继续努力 平均分>90 奖励电脑 if语句的嵌套//选择嵌套的特点: 1.嵌套进去的一定是一个完整的代码块 2.一定嵌套在一种情况之中 写内部代码块的时候 完全不用去考虑外部
平均分>=85 {
再看数学成绩>85 如果不大于 则奖励数学作业
看语文成绩>85 如果不大于 则奖励语文作业
}
平均分<85{
先挨打
再看数学成绩>85 如果不大于 则奖励数学作业
看语文成绩>85 如果不大于 则奖励语文作业
}
1.客户中心
1.修改信息
1.修改姓名
2.修改密码
2.展示信息
2.商品中心
1.展示商品
2.添加商品
3.结算中心
1.展示购买的商品
2.清单
switch选择结构 switch(变量名){ case 值1: 如果变量等于值1 执行的代码块; break; case 值2: 如果变量等于值2 执行的代码块; break; default: 如果以上都不满足 则执行的代码块; } break:结束当前代码块 跳出当前代码块 执行当前代码块后面的代码 switch中每种情况应该添加break; 而单独的if语句 不能使用break 除非外面嵌套有循环
continue:结束本次循环 执行下一次循环(continue后面的代码本次不再执行) 使用在嵌套在循环中的判断代码块里 (在这个代码块里 continue后面不能有语句) 注意点: 1.在某一个情况中 break后不能再添加任何语句 2.case各个情况 可以换位置 3.一般做区间判断可用if结构 等只判断 尤其是菜单可用switch 4.default可以和case换位置 但是一旦不是最后一条语句 就要加break 5.byte short int char String enum(枚举类) switch结构的嵌套 1.嵌套进去的一定是一个完整的代码块 2.一定嵌套在一种情况之中 break之前
循环结构if(判断条件){
执行的代码
}
while(判断表达式){ 循环体}
打印100遍
特点: 先判断 为真则执行 为假则退出 执行完后会继续判断 为真则执行 为假则退出 *先判断 后执行 有可能一次都不执行
思路: 判断条件的变量设置初始值 1.找判断条件 2.找循环体 3.让判断条件的变量迭代 debug:测试 1.在怀疑会出现问题的代码前 或者 你希望以下代码能够单步执行 左侧双击两下 打断点 2.点击工具栏中 debug图标(臭虫) 进入debug模式 3.F5 单步跳入执行 F6 单步执行 观察右侧变量变化情况 练习: 小明考试没有及格 需要重新学习 再考试 如果还不及格 继续重新学习 再考试 直到及格为止
判断条件:成绩是否及格(int String)
循环体:学习 考试 获取成绩
do{ 循环体}while(判断条件);*先执行 再判断 至少执行一次 int score = 1000; do { System.out.println(“继续学习\t参加考试\t输入成绩”); score = input.nextInt(); }while(score<60); 练习: 跑10圈 每圈过程中询问是否需要喝水 如果需要 则输出一个喝水 如果不需要则 继续跑步 for循环 一般来说 循环次数固定的循环 可以使用for循环
for(变量的初始值;判断表达式;变量的迭代情况){
循环体
}
变量初始值可放在for外面 迭代情况 可作为循环体放入大括号
判断表达式不能省略 否则死循环
for循环初始变量一般从0开始计数 为了和后期的索引进行匹配
for(int i =0;i<几次;i++){
//当前运行第(i+1)次
}
循环结构自我总结`
循环结构自我总结`
while(判断添加){
执行的代码
}
do{
执行的代码
}while(判断条件);
for(变量的初始值;判断表达式;变量迭代情况){
}
分支语句
if(判断条件){
执行的代码
}
if(判断条件){
执行的代码
}else {
执行的另一种代码情况
}
switch(变量名){
case 1:
变量等于值1执行的代码
case 2:
变量等于值2执行的代码
case 3:
变量等于值3执行的代码
default:
执行以上不同情况的代码
}
增强for循环
适用场景:只适合对于数组或者集合一类的数列的遍历展示
如果要增删改原数组中的数据还是需要使用普通for循环
for(数组类型 变量名 : 数组名、集合){
}
jvm 虚拟机 jre 运行环境 jdk java的开发工具包
基本数据类型: byte 字节型 int 整型 short 短整型 long 长整型 char 字符型 boolean 布尔型 double 双精度浮点型 float 单精度浮点型
引用数据类型: 数组 string 类
break:结束次循环
continue:结束本次循环,进入下一次循环
数组
数据存储在内存中的形式虚拟机对内存进行了划分:1.寄存器:给cpu使用 与开发无关2.本地方法栈:虚拟机在使用操作系统功能时 使用 与开发无关3.方法区:存储所有编译后的class文件 里面的代码内容 从方法区中去获取各种要素(类 方法)的方式称之为反射4.堆内存:存储对象地址信息 只要看到new 一定进入堆内存创建对象5.栈内存:方法执行时使用的内存 只要方法运行 一定进栈 (先进后出 后进先出)
数组的概念: 存储多个同一类型数据的容器 优势:对于数据的存储 管理 操作更加方便 注意点:同一类型 是在内存中开辟一连串连续的空间 声明时候就必须确定数组长度
声明语法: int[] 数组名 = new int[长度]; int[] 数组名 = new int[]{值1,值2,值3…}; 三要素:类型 数组名 长度 数组名[下标] 数组长度=最后一个元素的下标+1
* 数组名[数组长度]——》数组越界 ——》空指针异常
ArrayIndexOutOfBoundsException
索引:每一个存储到数组的元素 都会自动拥有一个编号 从0开始 向后依次累加
属性:数组名.length;——->数组长度
最后一个下标:数组名.length-1
//录入五名同学的成绩 先展示一遍 然后排序 排序后按照从大到小展示
引用数据类型 : 使用对象 就是在使用对象的地址 叫做地址的引用 称之为引用数据类型自定义类型int int[]
正确的数组复制 应该复制里面的元素
int[] score = new int[] {65,89,77,100,92};
//int[] oldScore = score;//赋的是一个地址 改变其中一个数组的值 另一个会跟这变
int[] oldScore = new int[score.length];
for(int i =0;i
}
//练习 int[] score = new int[] {65,89,77,100,92};先对原数组进行从小到大的排序创建新数组 要求比老数组多一个空间 获取老数组的数据内容 用户输入待插入的数据 要求插入到合适的位置(从小到大)
菱形
//二维数组:
int [] num1 =new int[3];
num1[0]=new int[4];
num1[1]=new int[5];
for(int i=0;i
}
}
数组排序:
拓展String类型的方法
String.equals(String)String.charAt(int)String1.compareTo(String2) -1 在字母表中 String1 在String2 前面 String1 bSDFsd String Basdfs 0 字母完全相同1 在字母表中 String1 在String2 后面 String1 fSDFsd String easdfs
String1.compareToIgnoreCase(String2) 忽略大小写来判断NullPointerException 空指针 找不到对象
方法method function概念: 在很多语言中 也称之为“函数” 指的是一段可以被重复调用的代码块 一般是具备一定功能实现的 他是一种实现代码重用的重要手段 就是将一个功能抽取出来 把代码单独定义在一对大括号当中 形成一个单独调用使用的方法语法: 访问修饰符 返回值 方法名(参数){ 方法体 } public static void main(String[] args) {
}
访问修饰符:public 公开的
static: 静态的 如果创建的方法是在main方法所在类中 一定要添加static 让这个方法先扫入内存 调用时才可能被找到
void: 如果没有固定的返回值 就添加void
如果有返回值 返回值是什么类型 就写什么类型 同时方法体的最后一句一定要使用return 去返回一个该类型的值 或者每种情况都定义了返回值
一个方法的返回值 只能有一个
方法名:按照标识符的起名规范 首字母还要小写 如果有后续单词 首字母大写 不要用到关键字
*参数:可有可无 参数是可以影响方法执行结果的变量
例如榨汁机 榨汁方法 苹果 —-》苹果汁 梨子——》梨子汁
注意点:方法不能相互嵌套 只能相互调用 方法一定存在于一个类中
调用方法需要注意的: 如果有返回值 那么调用方法就是在调用返回值 1.使用同类型的变量接收这个方法 2.直接使用输出语句输出这个方法 3.根据返回值的特性直接参与逻辑运算 练习:做一个方法 依次录入五名同学的成绩和性别
方法参数的使用注意: 形式参数:设计方法时 确定有变量会影响执行结果 可以在方法的小括号中定义一个变量(参数) 但是在设计方法时 这个参数是没有真实值的(只是标记) 称之为形参 后续设计方法时 把这个形参变量当成已知量来使用就行 实际参数:在调用方法前 应该根据设计的方法 考虑是否提前准备好对应类型的变量和值 在调用方法时 直接把这个有真实值的变量放入小括号中 他会在调用时传递给形参 那么这个有真实值的变量 就是实参
方法的参数和返回值是两个独立的概念
用不用参数 关注有没有变量会影响方法的执行结果
用不用返回值 取决于最终希望如何用这个方法
面向对象的开发方式
面向对象的开发方式<br />面向过程 代码不利于重复利用 不利于维护 不利于扩展<br />面向过程思考方式:直接以最终的目的为结果 去编写代码<br />面向对象思考方式:抽出这个过程中单独的方法和变量 封装成为独立的类<br />面向对象的实现过程:就是去编写设计类
封装:面向对象开发思想的三大特性之一 所属一个类的方法和属性从主类中提取出来 单独生成一个类来保存管理 再次使用前应该创建这个类的对象 通过对象去调用 类和对象 类:具备相同属性和方法的一类对象的集合 对象:具体存在的真实事物 类是对象的抽象化 对象是类的实例化
class User{
}
User u = new User();地址
int i = 5; 5
类: 变量 方法 main方法所在的类 其中的全局变量 和自定义的方法 都需要添加static 成员变量&局部变量 定义的位置: 局部变量定义在方法中 成员(全局)变量定义在类中 初始化不同: 成员变量会有默认的初始值 局部变量需要赋值才能使用 在内存中的位置不同: 成员变量存储在对象中(堆里) 局部变量存储在方法中(栈里) 声明周期: 成员变量随着对象的创建出现在堆中 随着对象的消失而从堆中消失 局部变量随着方法的执行出现在栈中 随着方法的出栈而消失 * 可取的类型: 基本数据类型 引用数据类型
程序开始执行的过程 1.所有的类文件编译成功后会读入到方法区中 2.执行main() 就会让main()先进栈 3.Students stu = new Students(); 在栈中声明Students的引用stu 在堆中开辟空间创建Students的对象 而栈中的stu指向这个空间地址 在堆中 为Students类中的成员变量赋默认值 在堆中 对象的方法保存放法区中的方法地址 4.使用stu的引用调用方法 会根据堆中保存的方法地址 去方法区中找到这个方法 进栈执行 5.方法中的局部变量被创建于栈中 赋值 执行 6.方法执行完后 出栈 局部变量消失 7.main()执行完出栈 程序结束 堆内存清除(对象空间清除 成员变量清除) 类型的扩充除了我们使用的八大基本数据类型+String以外我们可以根据对象的属性和方法 创建类型来表示这一类对象 这个类称之为自定义类型属于引用数据类型 使用方式类似于其他类型调用类中的方法和属性 使用 . 关键符号
this:代表当前类的对象 (最终在其它类中new的对象)
班级类 班级名称 人数 班主任 展示以上信息的方法 测试类 创建班级类的对象 c1 改变相关信息进行展示 c2 改变信息进行展示 展示一遍c1 设计类 创建类 声明属性(成员变量) int count; 定义方法(方法中可以去使用成员变量 当成已知量使用) 测试类 main()方法中 类名 对象名= new 类名(); 对象名.成员变量=新的值; 对象名.方法 构造方法 类名 对象名= new 类名(); 当一个对象被创建的时候 会执行一个方法 这个方法就叫做构造方法
public 类名(){
}
构造方法可以重载
- 如果不写构造方法 代码会执行默认的一个无参构造方法
一旦我们编写了构造方法(无论是有参还是无参) 那么默认的那个无参方法就失效了
构造方法和普通方法的区别:
1.构造方法没有返回值
2.构造方法的名称直接是类名 普通方法符合标识符命名规范即可
3.普通方法在调用时执行 构造方法在new对象时自动执行
调用:this+()
进一步封装 目的:类中的成员变量 可以被随便访问和修改 危险性很大 1》可以设置为private 变成私有的成员变量 一旦定义为私有成员变量 既不可以访问也不可以修改 2》添加两个方法 一个用来对这个成员变量赋值 一个用来对这个成员变量取值 赋值方法 public void setClassName(String className) { //可以编写控制赋值的逻辑代码 this.className = className; } 取值方法 public String getClassName() { //可以编写控制查看的逻辑代码 return className; } 3》通过测试类中new的对象去调用 快捷键创建封装成员变量方法Alt+shift+s—->找 Getters Setters 快捷键创建构造方法Alt+shift+s——>constructor using Filds二
continue 结束本次循环 执行下一次循环break 直接结束当前模块return 结束当前模块 同时返回值
&&&& 带参方法 设计带参方法是 参数就是个空壳 当成已知量来操作就行了 不用关心他的真实值 调用方法时 要先去观察方法是否有形参 如果有的话 提前准备好同类型的真实值作为实参 再调用这个带参方法 同时把你准备的真实值(变量名)放入小括号作为实参&&&& 封装成员变量 1.为了保证数据安全 将成员变量私有化 2.为了能够[查看] 所以提供一个方法 如果是没有返回值的方法 那么我们只能简单的查看一下 不能对值进行使用 所以我们考虑使用带返回值的方法 返回这个成员变量 3.为了实现对于[修改]的控制 可以定一个方法 对于私有的成员变量进行赋值 但是这个值的来源不能确定 所以不如以形参的方式表示要赋的值(不关心真实值和值的来源 只当成是已知量来设计方法)
使用过程
想要查看 调用getxxx() 有返回值
想要修改 调用setxxx(参数)
&&&& 构造方法 1.语法 2.可以实现重载 3.如果自定义了构造方法 默认的无参构造就会失效 4.new的时候 调用只能调用已有的构造方法(参数匹配) 5.构造方法之间的调用 多参数构造调用少参数或者无参数 如果少参或者无参调用多参 要提前准备好实参(static) 调用构造方法的语法 this() 由小括号中的参数列表决定调用哪个构造方法 调用构造方法的语句一定在第一行 * 一定要保留至少一个构造方法没有调用其他构造方法 作为出口
练习: 创建学生Students类型 String id String name char sex 无参的 一个参数 两个参数 三个参数 show(){ 展示学员的各种属性}
创建三个对象 s1 无参 s2 两个参数 s3 三个参数
匿名对象 没有在栈中声明类型的引用 而直接使用new来在堆中开辟空间(对象) 我们可以直接使用这个空间的方式 叫做使用匿名对象 例如:new Students().setId(“0100”); 特点:匿名对象只能使用一次 就成为了堆内存中的垃圾空间
所有的引用数据类型变量 一定要new之后才能使用 否则会报空指针异常 升级保存学生的姓名 和成绩 能够根据姓名 赋值成绩 能够根据姓名 获取成绩String[] names = new String[5];int[] scores = new int[5];
创建引用数据类型的数组
里面每一个元素都是引用数据类型 都一定要单独new空间
for (int i = 0; i < stus.length; i++) {
System.out.println(“请输入第名同学的姓名”);
String name = input.next();
System.out.println(“请输入第名同学的成绩”);
int score = input.nextInt();
//一定要对每一个引用数据类型的元素new对象
stus[i] = new Students();
stus[i].setName(name);
stus[i].setscore(score);
}
引用数据类型作为方法的参数引用数据类型作为方法的返回值
引用数据类型的数组 作为方法的参数引用数据类型的数组 作文方法的返回值
总结:在test测试类中要new一个对象才能在里面调用它的属性和修改
private 私有的 public 公开的 static 静态的 void 无效的
在一串字符中插入一个字符:
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner input=new Scanner(System.in);
//定义变量
char[] nums=new char[9]; //定义一个数组
char insertNum;//定义一个字符
int index=nums.length-1;//定义插入的数等于这个数的下标后面一位
nums[0]=’a’;
nums[1]=’b’;
nums[2]=’c’;
nums[3]=’e’;
nums[4]=’f’;
nums[5]=’p’;
nums[6]=’u’;
nums[7]=’z’;//定义8个字符
System.out.print(“请输入一个字符:”);//提示输入一个字符
insertNum =input.next().charAt(0);//接收
//找到输入字符应放在数组下标的位置
for(int i=0;i
break;
}
}
//把index之后的元素全部往后移
for(int i=nums.length-2;i>=index;i—){
nums[i+1]=nums[i];
}
//把插入字符赋值
nums[index] =insertNum;
//重新打印数列
for(int i=0;i
}
}
}
增强for循环的语法:
for(数据类型,方法名 :数组和集合){
}
封装
面向对象三大特性之一 优点: 提高了代码的复用性 提升了开发效率 提高了代码的可扩展性 隐藏了实现的细节 保证了代码的安全性 表现: 方法就是一种非常基本的封装体 类也是一个封装体 成员变量进行封装
实体类:由对象抽象而来 通过创建对象使用
标准的实体类编写规范: JavaBean
类的名称一定要有意义 可以描述一定事物
类中的所有属性都必须用private进行封装 提供getXXX() setXXX(参数)
类中可以提供无数个构造方法 但是一定要提供一个无参构造方法
类种方法尽量不出现输出语句 所有的内容或都通过返回值
* 可以添加toString()来实现对象的展示
继承: 面向对象三大特性之一 概念:描述类之间的所属关系的 被抽取的类称之为子类 生成的新类称之为父类 使之子类具备父类相同的属性和方法 特点:判定两个类是否应该是继承关系 只需要套用is-a的关系语句 紫罗兰是一株植物 大象是一个陆地动物 狗是一只宠物 <br />语法:<br /> 创建一个类 预备作为父类<br /> public class 子类 extends 父类{<br /> <br /> }<br />关联:<br /> 1.子类中可以直接使用父类的成员变量和成员方法<br /> super.成员变量 super.成员方法()<br /> super() 调用父类构造方法<br /> 如果调用父类构造方法 一定要放在第一条语句<br /> 不能在一个构造方法中 同时调用本类其他构造方法和父类构造方法<br /> 2.其它类中创建的子类对象可以直接调用父类的成员变量和成员方法<br /> new子类对象 直接使用子类对象调用即可<br /> 3.一个类只能继承一个类作为他的父类 称之为单继承 可以向上或者向下无限继承<br />注意:<br /> 1.调用子类和父类重名的成员变量 代码先在子类中查找 如果有则展示子类中该成员变量的值 如果没有 则展示父类中该成员变量的值 如果都没有 则报错<br /> 2.调用方法 也会先在子类本类中查找 如果没有的话才去父类中查找 如果都没有 就报错<br /> 3.对于子类中和父类中 方法名相同 参数及参数列表都相同 但是方法体不同的方法 我们称之为 子类对于父类方法的重写<br /> <br />重写与重载的区别<br />重载:两同一不同 同一个类中 相同的方法名 参数不同<br /> 静态调用<br />重写:在子父类继承关系中 相同的方法名 相同的参数 方法体不同<br /> 子类重写方法的返回值类型 小于等于 父类方法的返回值类型<br /> public --protected --(default) --private <br /> 父类的成员变量和成员方法至少需要protected及以上的访问修饰符修饰 才能被子类对象访问到<br /> 使用protected修饰的父类成员变量及成员方法 只能被子类访问(受保护的)<br /> 动态调用
&&&& 子父类构造方法调用&&&& 子类重写父类方法 重写和重载的区别&&&& 子父类同名的成员变量的访问 练习:SchoolPerson
Students
姓名 年龄 班级 成绩
学习的方法study()
show()
Teachers
姓名 年龄 班级 科目
work()
show()
抽象类 abstract 方法重写关系比较紧密 概念:从逻辑意义上来说 有一些类不应该被new成实体对象 这些类就叫做抽象类
public abstract class 类名{
}
抽象类不能再被实例化
抽象类应该有自己的子类
抽象方法 public abstract 返回值类型 方法名();
抽象方法 不能有方法体
抽象方法必须由当前类的子类重写
* 抽象方法只能存在于抽象类中
- 抽象类可以做父类 也可以做子类
* abstract 不能和private final(常量) static一起使用
抽象这个概念的运用 多半是在设计类的时候使用
public abstract class L{
public abstract 返回值类型 方法名();
public void show(){
}
}
练习: 以抽象类的设计思想 完成以下代码需求 商店中有很多的商品goods 体育类的Sport 制造日期buildDate 重写展示商品信息的方法 要求展示出制造日期 食物类的Food 保质期liveDate 重写展示商品信息的方法 要求展示出保质期 学习用具Study 品牌breed 重写展示商品信息的方法 要求展示出品牌 除去各自属性外 他们都具备类别type 名称name 价钱price属性 都有一个展示商品信息的方法show()
体育类的Sport 制造日期buildDate 类别type 名称name 价钱price属性
展示商品信息的方法
食物类的Food 保质期liveDate 类别type 名称name 价钱price属性
展示商品信息的方法
学习用具Study 品牌breed 类别type 名称name 价钱price属性
展示商品信息的方法
我的总结:
关键字: abstract(抽象的) private(私有的) protected(受保护的 )
语法:
public abstract class L {
public abstract 返回值类型 方法名();
public void show(){
}
}
重写与重载的区别
重载:两同一不同 同一个类中 相同的方法名 参数不同
静态调用
重写:在子父类继承关系中 相同的方法名 相同的参数 方法体不同
子类重写方法的返回值类型 小于等于 父类方法的返回值类型
public —protected —(default) —private
父类的成员变量和成员方法至少需要protected及以上的访问修饰符修饰 才能被子类对象访问到
使用protected修饰的父类成员变量及成员方法 只能被子类访问(受保护的)
动态调用
this 本类的调用<br /> super 子类中调用父类的方法<br /> 总结java中的super和this关键字<br />知识点: 在java类中使用super引用父类的成分,用this引用当前对象 this可以修饰属性.构造器.方法 super可以修饰属性.构造器.方法 <br /> 继承:extends<br /> 语法:<br /> 创建一个类 预备作为父类<br /> public class 子类 extends 父类{<br /> <br /> }
util:辅助类
可以new调用里面的方法和属性
1、continue
continue用于结束循环体中其后语句的执行,并跳回循环程序块的开头执行下一次循环,而不是立刻循环体。
2、break
break的作用是跳出当前循环块(for、while、do while)或程序块(switch)。在循环块中的作用是跳出当前正在循环的循环体。在程序块中的作用是中断和下一个case条件的比较。
3、return
终止一个函数的执行,并返回 expression 的值。
4、打标记
一般做return的替代,如果程序的循环中使用的return会终止整个程序之后的指令,无法程序操作。
打标记的作用是在循环之前设置参数boolean,当Boolean的值改变时,对应的循环输出也相应的发生改变。
打标记的使用
例子:
//根据用户输入的随意正整数,判断其各个位子的数之和是偶数还是奇数
import java.util.Scanner;
public class bjdemo{
public static void main(String[] args){
Scanner input=new Scanner(System.in);
//提示用户
System.out.println(“请输入一个随意正整数:”);
int num=input.nextInt();
//循环做拆分,在这之前可以先判断该数的位数,用循环做一直除以10一直到等于0时,记录执行次数,该次数-1就是该数的位数。
int n,sum;
sum=0;
for(int i=1;;i++){
sum=num/n%10;
if(num/n==0){
break;
}
n=n*10;
}
boolean boo=true;//true代表该数的各个位子之和为偶数,false则为奇数
if(sum%2==0){
boo=true;
}else{
boo=false;
}
if(boo){
System.out.println(num+“是偶数”);
}else{
System.out.println(num+“是奇数”);
}
}
}
循环录入商店中的各个商品 展示所有的商品
多态: 面向对象三大特性之一 父类的引用指向子类的对象
Goods g=new Food();
g.show();
对比:
Food food=new Food();
food.show();
使用多态的条件:
#一定是在继承关系中
#父类的引用,指向子类的对象
#子类一定要重写父类的方法
封装:
面向对象三大特性之一
优点:
提高了代码的复用性 提升了开发效率 提高了代码的可扩展性
隐藏了实现的细节 保证了代码的安全性
表现:
方法就是一种非常基本的封装体
类也是一个封装体
成员变量进行封装
实体类:由对象抽象而来 通过创建对象使用
标准的实体类编写规范: JavaBean
类的名称一定要有意义 可以描述一定事物
类中的所有属性都必须用private进行封装 提供getXXX() setXXX(参数)
类中可以提供无数个构造方法 但是一定要提供一个无参构造方法
类种方法尽量不出现输出语句 所有的内容或都通过返回值
可以添加toString()来实现对象的展示
继承:
面向对象三大特性之一
概念:描述类之间的所属关系的 被抽取的类称之为子类 生成的新类称之为父类 使之子类具备父类相同的属性和方法
特点:判定两个类是否应该是继承关系 只需要套用is-a的关系语句 紫罗兰是一株植物 大象是一个陆地动物
狗是一只宠物
语法:
创建一个类 预备作为父类
public class 子类 extends 父类{
}
关联:
1.子类中可以直接使用父类的成员变量和成员方法
super.成员变量 super.成员方法()
super() 调用父类构造方法
如果调用父类构造方法 一定要放在第一条语句
不能在一个构造方法中 同时调用本类其他构造方法和父类构造方法
2.其它类中创建的子类对象可以直接调用父类的成员变量和成员方法
new子类对象 直接使用子类对象调用即可
3.一个类只能继承一个类作为他的父类 称之为单继承 可以向上或者向下无限继承
注意:
1.调用子类和父类重名的成员变量 代码先在子类中查找 如果有则展示子类中该成员变量的值 如果没有 则展示父类中该成员变量的值 如果都没有 则报错
2.调用方法 也会先在子类本类中查找 如果没有的话才去父类中查找 如果都没有 就报错
3.对于子类中和父类中 方法名相同 参数及参数列表都相同 但是方法体不同的方法 我们称之为 子类对于父类方法的重写
重写与重载的区别
重载:两同一不同 同一个类中 相同的方法名 参数不同
静态调用
重写:在子父类继承关系中 相同的方法名 相同的参数 方法体不同
子类重写方法的返回值类型 小于等于 父类方法的返回值类型
public —protected —(default) —private
父类的成员变量和成员方法至少需要protected及以上的访问修饰符修饰 才能被子类对象访问到
使用protected修饰的父类成员变量及成员方法 只能被子类访问(受保护的)
动态调用
&&&& 子父类构造方法调用
&&&& 子类重写父类方法 重写和重载的区别
&&&& 子父类同名的成员变量的访问
练习:
SchoolPerson
Students
姓名 年龄 班级 成绩
学习的方法study()
show()
Teachers
姓名 年龄 班级 科目
work()
show()
抽象类 abstract 方法重写关系比较紧密
概念:从逻辑意义上来说 有一些类不应该被new成实体对象 这些类就叫做抽象类
public abstract class 类名{
}
抽象类不能再被实例化
抽象类应该有自己的子类
抽象方法
public abstract 返回值类型 方法名();
抽象方法 不能有方法体
抽象方法必须由当前类的子类重写
抽象方法只能存在于抽象类中
抽象类可以做父类 也可以做子类
abstract 不能和private final(常量) static一起使用
抽象这个概念的运用 多半是在设计类的时候使用
public abstract class L{
public abstract 返回值类型 方法名();
public void show(){
}
}
练习:
以抽象类的设计思想 完成以下代码需求
商店中有很多的商品goods
体育类的Sport 制造日期buildDate 重写展示商品信息的方法 要求展示出制造日期
食物类的Food 保质期liveDate 重写展示商品信息的方法 要求展示出保质期
学习用具Study 品牌breed 重写展示商品信息的方法 要求展示出品牌
除去各自属性外 他们都具备类别type 名称name 价钱price属性
都有一个展示商品信息的方法show()
体育类的Sport 制造日期buildDate 类别type 名称name 价钱price属性
展示商品信息的方法
食物类的Food 保质期liveDate 类别type 名称name 价钱price属性
展示商品信息的方法
学习用具Study 品牌breed 类别type 名称name 价钱price属性
展示商品信息的方法
循环录入商店中的各个商品
展示所有的商品
根据品类展示同类型商品
参数 用户输入的名称 返回具体的某商品对象
调用子类特有的方法 父类没有这个方法 是某个子类自己的方法
多态:
面向对象三大特性之一
父类的引用指向子类的对象
Goods g = new Food();
//使用父类的引用调用当前子类重写之后的方法
g.show();
查找顺序:
先找父类看有没有这个方法 如果没有 直接报错
执行顺序:
如果父类有这个方法 再去查看子类有没有重写 如果子类有重写方法 执行子类重写 如果子类没重写 执行父类方法
如果只有父类有 子类没有 没关系
如果只有子类有 父类没有 会报错
如果想要调用子类特有的方法 就一定需要获取子类自己的对象
就要判断 当前父类的引用中到底保存的是哪个子类
g instanceof 子类类型 返回true/false
如果返回真 子类类型 变量名 = (子类类型)g;
最后使用子类变量名.特有的方法;
对比:
Food food = new Food();
food.show();
如果子类没有 找父类 父类没有就报错
如果只有父类有 子类没有 没关系
如果只有子类有 父类没有 没关系
使用多态的条件:
一定是在继承(接口)关系中
父类的引用 指向子类的对象
* 子类一定要重写父类的方法
&&&&多态运用在方法的参数中
public void xxxx(Goods good){}
声明父类的引用作为方法的形参
Food food = new Food(xx,xx,xx,xx);
xxxx(food);
调用方法前创建父类的子类对象作为方法的实参
在xxxx方法中 依然使用形参good.重写的方法()
当前父类的引用就是这个子类对象
可以通过父类引用调用当前子类重写的方法
if(good instanceof Food)
可以通过instanceof去判断当前父类引用中保存的是哪个子类对象
Food food = (Food)good;
再进行强转
food.子类特有的方法()
强转后就可以运用子类的对象调用子类特有的方法
&&&&多态运用在方法的返回值中
可以在不清楚到底返回哪个子类对象的时候 使用父类的引用作为方法的返回值
public Goods xxx(){}
当调用有父类引用作为返回值的方法的时候 就要准备一个父类的变量接收
Goods goods = xxx();
可以使用goods.重写的方法()
当前父类的引用就是这个子类对象
可以通过父类引用调用当前子类重写的方法
if(goods instanceof Food)
可以通过instanceof去判断当前父类引用中保存的是哪个子类对象
Food food = (Food)goods;
再进行强转
food.子类特有的方法()
强转后就可以运用子类的对象调用子类特有的方法
static 静态的
static
静态的
概念:可以用于全局属性和全局方法的声明 可以避免对象实例化的限制 由static修饰的成员变量变为所有当前类的对象共享 这样的属性保存在全局数据区中 (在方法区中)(成员变量)
特点:
由static修饰的成员变量 可以被所有对象访问 也可以由类名直接访问
由static修饰的方法 可以被所有对象访问 也可以由类名直接访问
注意事项:
由static修饰的方法内不能有this关键字
由static修饰的方法内 可以调用由static修饰的方法和属性 如果是普通方法和属性 需要new对象调用
* 非static修饰的方法内可以调用static修饰的方法和属性
练习:
创建图书类 用一个变量来保存图书的数量
public class Books{
public static int count;
public Books(){
count++;
}
}
main(){
Books b1 = new Books();
//Books.count++;
Books b2 = new Books();
//Books.count++;
}
代码块
概念:在程序之中使用{}定义起来的一段程序
分类:
普通代码块:if(){} while(){}
构造代码块:在实体类中使用 优先于构造方法执行 每次实例化都执行一次
静态代码块:优先于构造代码块 多次实例化只执行一次
如果没有实例化 也没有通过本类类名调用过本类静态成员 那么静态代码块未执行
主类中使用 会先执行main() 但是不会执行内部代码 而是优先执行static{}
如果没有main() 那么static{}不会执行
同步代码块
先调用main()但不执行内部代码
主类中的静态代码块
实体类的静态代码块
实体类构造代码块
实体类构造方法
执行main()后续代码
final 最终的 与abstract相对的关键词
1.数据型常量 “中国” 5
2.自定义常量 final int COUNT = 5; i=6;(报错)
常量的赋值
建议使用静态常量 public final static String name = “java”;
建议使用静态代码块 static{ public final static String name = “java”; }
对于局部常量 可以通过形参赋值 每次是不同的常量空间
对于全局常量 不可以通过形参赋值
由final修饰的方法 为 方法的最终形态 必须要有方法体
由final修饰的类 不能有子类继承 称之为太监类 所以内部不能放抽象方法
由final修饰的方法 可以放在普通类中 如果被继承 该方法不能被重写 只能使用super调用执行
final可不可以修饰引用数据类型 可以
final Users u = new Users(“张三”,18);
修饰的引用数据类型引用名 不能再指向其他空间 不能new
但是当前对象中的属性可以重新修改赋值
u.name = “李四”;
u.age = 20;
u = new Users(“李四”,20);//报错
修饰的引用数据类型的数组 不能重新new
但是里面的每个元素可以反复new
final Users[] u = new Users[5];
u[0] = new Users();
u[0] = new Users();
u = new Users[6];//报错
接口
接口
概念: 首先是一个类 interface
一种定义好的标准和规范 满足 has a
jdk1.7之前 他是一系列抽象方法的集合
public interface PrintOut {
//在接口中 普遍使用的是抽象方法 可以省略abstract
public void printMethod();
}
public class ColorPrint implements PrintOut{}
继承一个父类 实现多个接口 不管是父类的还是接口的抽象方法 实现类都要重写
作为接口 只能够继承接口作为父类 而且可以实现多继承 (不能实现接口和普通类 也不能继承普通类)
public interface i1 extends 接口名1,接口名2
如果接口1和接口2有个同名的默认方法 那么子接口i1就一定要重写这个方法 否则报错
如果子接口i1和父接口1有一个同名的默认方法 那么实现类会依据就近原则 执行子接口i1的方法
如果子接口i1重写了父接口1的方法 那么一定执行的是重写后的方法
public interface i1 extends 普通类 //错误
public interface i1 implements …//错误
public class ColorPrint extends Paper implements PrintOut{
@Override
public void printMethod() {
// TODO Auto-generated method stub
System.out.println(“我是彩色输出模式”);
}
}
执行:
父类引用会先去判断方法是否存在于父类中 否则报错 是则再判断子类有否重写这个方法 有则执行重写后的方法 无则执行父类方法
接口引用直接去调用当前实现类内部实现的这个接口中的抽象方法
建议 无论使用接口引用 还是使用父类引用 都通过判断后强转为实现类(子类)的对象
注意点:
接口中不存在变量 都是默认的静态常量 所以要求一定要赋值 int COUNT = 5;
如果希望接口中的方法有完整的方法体 应该使用default修饰方法 才可以添加方法体
public default void 方法名(){} 随意调用
可以添加静态方法 但是只能由当前接口类的类名调用 不能使用实现类的对象调用
public static void 方法名(){} 只能由当前接口类名调用
* 在jdk9及以上 接口中可以对于方法进行私有化 私有化的方法必须有完整方法体
private void 方法名(){} 只能在本接口中调用
练习:
接口重构银行系统
银监会
介绍银行功能
银行类
保存用户功能
银行A 银行B
用户类
银监会和银行类 如果有同名的默认方法 结果怎样
打印机
打印机接口中 定义了一个打印的功能
Paper
定义了一个接收纸张的抽象方法
彩色打印机
黑白打印机
面向对象三大特性
封装 继承(接口) 多态
(extends) implements
总结:
对面对象的三大特征:
1:封装:所属一个类的方法和属性从主类中提取出来 单独生成一个类来保存管理 再次使用前应该创建这个类的对象 通过对象去调用
表现: 方法就是一种非常基本的封装体 类也是一个封装体 成员变量进行封装
2:继承(extends):描述类之间的所属关系的 被抽取的类称之为子类 生成的新类称之为父类 使之子类具备父类相同的属性和方法 满足 is-a 特点:判定两个类是否应该是继承关系 只需要套用is-a的关系语句 紫罗兰是一株植物 大象是一个陆地动物
接口(implements):在java中想实现多继承就只能用接口
概念: 一种定义好的标准和规范 满足 has a
Java语法中,接口可以继承接bai口,抽象类可以实du现接口,zhi抽象类可以继承实体类。
Java语法中,使用interface定义一个接口,使用class定义一个类,使用关键字abstract + class定义一个抽象类。
3:多态(instanceof ):父类的引用指向子类的对象
(俗话:使用父类表示子类,父类可以表示他的哪一个子类)
s三元运算符
例题:
常见的API
Object类
Object类 所有类的父类<br /> class 所属什么类型 Class类型
toString()
返回:getClass().getName() + ‘@’ + Integer.toHexString(hashCode())
返回一个由其中的对象实例的类的名称的字符串+@ +和对象的哈希码的无符号的十六进制表示
equals()
引用数据类型(除String) 都可以重写Object的equals() 按照我们自己的判断需求重写
如果只有基本数据类型 可以直接通过快捷键导入自带的equals()重写方法
String类型
String保存的是固定不可改变的值 这个值存在于常量池中 可以被多次获取
重新赋值是去查找指向另外的常量池中的值 而放弃原值 原值依然在常量池中
String str = “abc”;
str = “ccc”;
String str1 = “ccc”;
str1 = “bbb”;
str == str1 true
== 如果用于基本数据类型的比较(值都在常量池中 不会有重复数据 相同的数据一定是同一个数据)
== 如果有用引用数据类型比较 比较的是地址
为了比较两个有空间String 可以使用equals() 只比较值
“中国” “java47班”
特点:
String类型本身的内容永不可改变
常量池中的字符串是可以共享的
字符串从效果上看 可以看成是char[] 但是底层原理是byte[]字节数组
String str = new String();
new String(“”);
new String(char[] c);
new String(byte[] b);
例如:
char data[] = {‘a’, ‘b’, ‘c’};
String str = new String(data);
String str = new String(“张三”);
判断字符串中是否有值 “” null
判断””
String.equals(“”)
String.isEmpty() 空—true
String.length()==0
判断null
String s ==null
练习: String str = “中国你好”;
以字符的形式一个一个逆序输出
学习方法规则:
第一 先注意是属于哪一个类
第二 方法名及作用
第三 方法参数和返回值
取字符
.charAt(int i)
判断顺序
.compareToIgnoreCase(String s) -1 0 1
.compareTo(String s)
判断相等
.equalsIgnoreCase(String s)
.equals(String s)
追加
+
.concat(String s)
通过元素找下标
.indexOf(“”) int
.indexOf(int i) 参数为字符编码值 a97 A65
.lastIndexOf() 从后面找
通过下标找元素
.subString(int 起始) 从这开始取一直数全部取完
.subString(int 起始 ,int 结束) [ )
去掉空格
.trim() 中间的空格去不掉
用新字符串替换旧字符串
.replace(‘’,’’)
.replaceAll(“旧”, “新”) 需要重新接受
转大写
.toUpperCase()
转小写
.toLowerCase()
将字符串根据某个标记进行切割 获得字符串数组
.split(String s) String[]
将一个字符串数组中的元素以某个标记进行连接 获取新的字符串
String.join(String s,String[] strs)
判断是否以。。。结尾(经常拿来判断那文件的格式)
.endsWith(“.txt”)
判断是否以。。。开头
.startsWith(String s)
判断字符串中是否包含某字符
.contains(‘字符’) boolean
String增强类
StringBuffer 线程安全的 效率低点
StringBuilder 线程不安全的 效率更高 常用
和String str = “abc”;
StringBuffer sb = new StringBuffer(); 默认16个字符的数组
sb = “中国你好”;
sb = 超出数组长度 先开辟要保存的值的长度+16 把值保存进去
sb.toString(); //转成String类型
.append(“”);追加
.delete(int,int);移除 (]
.insert(int,””);插入
.replace(int 起始,int 结束, 替换内容);
.reverse(); 逆序输出
Math
Math.abs(参数);绝对值
ceil(参数);向上取整 考虑正负号
floor(参数);向下取整 考虑正负号
pow(double a,double b);a的b次方
sqrt(参数);开平方
random();随机数 [0,1)
round(参数);对参数进行四舍五入 long
正数 四舍五入 +0.5 向下取整
负数 五舍六入 +0.5 向上取整 -5.6 -5.1 -6
保留小数
BigDecimal类
//不推荐以此方式创建对象
// BigDecimal bd = new BigDecimal(8.97);
// System.out.println(bd);
//可以使用 不推荐
// BigDecimal bd = new BigDecimal(“8.97”);
// System.out.println(bd);
BigDecimal bd = BigDecimal.valueOf(8.9565);
System.out.println(bd);
常用方法 文档
System.out.println(bd.add(bd1));//加法
System.out.println(bd.subtract(bd1));//减法
System.out.println(bd.multiply(bd2));//乘法
System.out.println(bd.divide(bd2));//除法
System.out.println(bd.divide(bd2, 2, BigDecimal.ROUND_UP));//保留小数位数 向上取整
System.out.println(bd.divide(bd2, 2, BigDecimal.ROUND_DOWN));//保留小数位数 向下取整
希望标准化输出数字
//控制输出格式
DecimalFormat df = new DecimalFormat(“0.0”);
System.out.println(df.format(8.656543));//返回的是String
//把String类型——Integer类型 int
DecimalFormat df1 = new DecimalFormat(“#.##%”); //乘以100再加%
System.out.println(df1.format(8.656543));//返回的是String
//df1.format(BigDecimal对象)
对比:
int类型:+.-.*./.%
Math类:可以进行基本的数字运算方法 指数 对数 三角函数等等 方法都是静态的
BigDecimal类:因为float double并不能表示精确的小数 在处理精度要求比较高的数据时 可以使用
例如:银行 高科技
DecimalFormat类:只负责控制结果的精度 通过创建模板 使用模板对象调用format() 返回String
练习:计算-10.5到9.6之间 绝对值大于6或者小于2.1的整数有多少个
7 8 9 -2 -1 0 1 2 -7 -8 -9 -10 12个
(DataBase)
Date日期类
java.util.Date 精确到毫秒
//构造方法一
Date date = new Date();
System.out.println(date);
//构造方法二 从1970年1月1日0点加上参数(毫秒数)
Date date1 = new Date(2785021349532L);
System.out.println(sdf.format(date1));
System.out.println(date1.getYear()+”年”+(date1.getMonth()+1)+”月”+date1.getDate()+”日”+date1.getHours()+”:”+date1.getMinutes()+”:”+date1.getSeconds());
格式化的输出日期时间
Date对象 按照指定的标准 转成String
创建格式
//SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy年MM月dd日 HH时mm分ss秒”);
System.out.println(sdf.format(date));
符合定义好的标准的String日期 转成Date对象
创建格式
//SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy年MM月dd日 HH时mm分ss秒”);
String str = “2019年5月1日 10时50分20秒”;
Date date3 = sdf.parse(符合定义好的标注的字符串)
获取从1970年1月1日0点到现在的一个时间差 long 毫秒数
System.currentTimeMillis()
等同于
new Date().getTime()
练习:
计算在世多少天
先获取1970年1月1日0点到此刻的毫秒数
再获取1970年1月1日0点到你出生日期的毫秒数
两者相减 /1000/60/60/24
Calendar 日历类 抽象类
创建方式
Calendar rightNow = Calendar.getInstance();
创建过程直接调用getInstance()静态方法
抽象类的对象中 将各种时间信息都封装为了静态成员变量 方便获取
System.out.println(rightNow.getTimeInMillis()); 获取1970年1月1日0点到目前的时间差
获取当前Calendar对象的具体某个时间信息
当前Calendar对象.get(Calendar.静态属性)
System.out.println(instance.get(Calendar.DATE));
为某个Calendar对象属性赋值
Calendar的对象.set(Calendar.静态属性,值);
instance.set(Calendar.YEAR,2013);
YEAR 年
MONTH 月-1
DAY_OF_MONTH 月中的某天 号
HOUR 小时 12时制
HOUR_OF_DAY 24小时表示法
MINUTE 分钟
SECOND 秒
DAY_OF_WEEK 周中的某天 星期几 周二:1 周三:2 周四:3 周五:4 周六:5 周日:6 周一:7 +1
add方法
可以对于指定日历字段的值进行加减操作
日历对象.add(Calendar.YEAR, -2); 如果希望向后累加 放正值 如果先前倒退 放负值
比较时间对象的先后
取毫秒值做差
取各种时间信息 从年开始比较
Arrays类
Arrays.sort(数组); 升序排列
Arrays.toString(数组); 标准格式输出
包装类 java.lang
概念:基本数据类型 引用数据类型
使用基本数据类型高效 但是并没有自己的对象 就不会有对应的方法可以使用
b bit B byte
byte———-Byte
short———Short
int————Integer
long———-Long
float———Float
double——-Double
char———-Character
boolean——Boolean
装箱:把一个数据从基本数据类型 转成对应的引用数据类型(包装类) 获取到他的对象
拆箱:从包装类对象转换为对应的基本类型
int num = 58;
//装箱方案一:
Integer it = new Integer(num);
//装箱方案二;
Integer it1 = Integer.valueOf(num);
//拆箱操作
int i = it.intValue();
//int l = “1235”; //错误
//从jdk1.5之后 可以执行自动装箱和拆箱
//自动装箱
Integer it2 = 58;
//自动拆箱
int it3 = it2;
基本数据类型与字符串之间的转换
//int l = “12345”;
int it4 = Integer.parseInt(“123”);
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy年MM月dd日 HH时mm分ss秒”);
Date date = sdf.parse(“2013年4月23日 18时39分56秒”);
byte b = Byte.parseByte(“”);
short s = Short.parseShort(“”);
int i = Integer.parseInt(“”);
long l = Long.parseLong(“”);
float f = Float.parseFloat(“”);
double d = Double.parseDouble(“”);
char c = “”.charAt(0);
boolean bool = Boolean.parseBoolean(“”);
计算在世多少天:
Scanner sc =new Scanner(System.in);
System.out.println(“请输入你的出生年月”);
String bornData=sc.next();//获取出生日期
SimpleDateFormat s=new SimpleDateFormat(“yyyy-MM-dd”);//字符串转换成date类型
Date dateid=s.parse(bornData);
Date now=new Date();//获取当前时间
long nowTime=now.getTime();//将当前date时间转换成毫秒
long bornTime=dateid.getTime();//将出生日期转换成毫秒
long time=nowTime-bornTime;//当前时间的毫秒减掉出生时间的毫秒就是活了多久的毫秒数
//根据毫秒判断出生与否,出生了就将毫秒时间转换成天数,一秒=一千毫秒,一分钟有60秒,一小时60分,一天24小时
if(time<0){
System.out.println(“您还没出生”);
}else{
System.out.println(“您已经活了”+time/1000/60/60/24+”天”);
异常
异常Exception
概念:
指的是程序在运行过程中出现的非正常情况 导致虚拟机非正常停止
NullPointerException ClassNotFoundException可以抛出并处理 处理后程序能正常执行
错误Error
错误:
指的是只有通过改变源代码才能解决的 非常严重的错误
抛异常是不能解决的 必须修改原代码
NoClassDeFoundError OutOfMemoryError
共同的父类 java.long.Throwable
Throwable——-Error
|
|————-Exception——-CheckedException编译期异常, RuntimeException运行时异常
可检查异常:在原代码中必须显式的进行捕获处理 这是编译期检查的一部分
parse(“”) 不处理 不编译 不能执行 ParseException
需要我们自己处理
运行时异常:通常是可以通过编码避免的逻辑错误 根据具体的需求来判断是否需要捕获 并不强制要求
parseInt(“”) 不处理 能编译 能执行 NullPointerException ClassNotFoundException
如果我们不处理 交给JVM处理
异常处理方案一:
throws:使用关键字在方法名参数的后面抛出可能的异常(他是以类的形式出现 只有类名)
可以抛出多个 用,隔开 没有顺序
异常处理方案二:
try{}catch(){}fianlly{}
try模块必须要有 包裹可能出现异常的代码
catch(){}匹配可能会出现的异常 至少有一个 可以有多个catch{} 子类异常放前面 父类异常放后面
finally{}可有可无 有一个 内部放如论如何都应该执行的代码 比如资源的关闭
异常的对象可以使用的方法
e.printStackTrace(); 打印异常发生后的栈信息 void
e.getMessage();展示出现异常的代码 String
e.toString(); 展示异常信息及日志时间 String
两种方法的区别:
当抛多个异常的时候 throws无顺序 try有顺序
异常体现模式 throws 类名 try对象
throws只展示异常的栈信息 而try可以自定义展示的内容
throw:
概念:可以使用这个关键字在指定的方法内抛出指定的异常
语法:throw new xxxException(“提示信息);
对于编译异常 依然要通过方案一或者方案二处理
对于运行时异常 依然可以不处理
throw和throws的区别:
throws 放在方法外部 后面跟的是异常的名称 跟多个异常
throw 放在方法内部 后面跟的是异常的对象 每次抛一个
异常关于继承关系注意点: 异常的子父类关系 子父类关系中的异常
关注在try模块中的顺序性
可以直接抛父类异常 就可以不用再抛子类异常 推荐还是去抛子类具体的异常
如果说父类的方法通过throws抛出了异常 子类重写这个被抛了异常的父类方法时
如果子类想要抛 则要求小于等于父类异常
也可以不抛
如果子类编写过程中出现了对应的编译异常 ,依然需要单独抛出。
如果子类重写父类方法时出现了编译异常 而父类该方法没有抛异常 子类可以通过try抛这个异常
自定义异常
预习 集合框架
总结:
object类
toString()
返回:getClass().getName() + ‘@’ + Integer.toHexString(hashCode())
返回一个由其中的对象实例的类的名称的字符串+@+和对象的哈希码的无符号的十六进制表示
equals()
引用数据类型(除String) 都可以重写Object的equals() 按照我们自己的判断需求重写
如果只有基本数据类型 可以直接通过快捷键导入自带的equals()重写方法
String类型
String保存的是固定不可改变的值 这个值存在于常量池中 可以被多次获取
重新赋值是去查找指向另外的常量池中的值 而放弃原值 原值依然在常量池中
String str = “abc”;
str = “ccc”;
String str1 = “ccc”;
str1 = “bbb”;
str == str1 true
特点:
String类型本身的内容永不可改变
常量池中的字符串是可以共享的
字符串从效果上看 可以看成是char[] 但是底层原理是byte[]字节数组
判断字符串中是否有值 “” null
判断””
.equals(“”)
.isEmpty() 空—thidingyrue
.length()==0
自定义异常
自定义异常 异常类中定义好的异常虽然够多 但是我们有时候需要按照自己的需求提示信息
创建自定义异常类:(运行时异常)
就是一个普通的类 去继承
*如果继承RuntimeException 就成功创建了一个运行时自定义异常
建议添加带一个字符串类型参数的构造方法
还要去调用带一个字符串类型参数的父类构造方法
//继承RuntimeException 自定义一个运行时异常
public class MyException extends RuntimeException{//Exception 编译期异常 public MyException(String message) { super(message);//getMessage() //最终这个message给到了 Throwable类的detailMessage私有变量保存 //getMessage()——>return detailMessage }}
使用自定义异常类
在我们希望抛出这个异常的地方去写
写throw new MyException(“用户名已存在”); 不用做其他任何处理(告诉虚拟机 此处就是我的异常情况)
或者 先通过throw抛自定义异常 再添加catch(MyException e)去匹配
或者 先通过throw抛自定义异常 方法 参数后面再通过 throws MyException
*对于自定义运行时异常 使用throw抛出后 可以不做任何处理(throws/try{})
if(name.equals(“张三”)) {
//告诉虚拟机 在此情况应该抛某个异常
throw new MyException(“用户名已存在”);
}
或者: try { if(name.equals(“张三”)) { //告诉虚拟机 在此情况应该抛某个异常 throw new MyException(“用户名已存在”); } } catch (MyException e) { // TODO: handle exception System.out.println(“进入了异常父类”); System.out.println(e.getMessage()); }
创建自定义异常类:(编译期异常)
就是一个普通的类 去继承
*继承Exception 就成功创建了一个编译期自定义异常
建议添加带一个字符串类型参数的构造方法
还要去调用带一个字符串类型参数的父类构造方法
//继承Exception 自定义一个编译期异常
public class MyException1 extends Exception{//编译期异常 public MyException1(String message) { super(message); }} //最终这个message给到了 Throwable类的detailMessage私有变量保存 //getMessage()——>return detailMessage
使用自定义异常类
在我们希望抛出这个异常的地方去写
写throw new MyException1(“用户名或密码有误”); (告诉虚拟机 此处就是我的异常情况)
但是 必须要处理 以下2选1
1.添加catch(MyException1 e)去匹配 处理
2.方法参数后面通过 throws MyException1 处理
*对于自定义编译期异常 使用throw抛出后 必须做处理(throws/try{})
String name = input.next();
System.out.println(“请输入密码”);
String pwd = input.next();
if(!name.equals(“李四”)||!pwd.equals(“123”)) {
try {
throw new MyException1(“用户名或密码有误”);
} catch (MyException1 e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println(e.getMessage());
}
}
1.明确要创建的是哪一类异常2.通过throw new 异常类名(参数); 如果是运行时异常 可以没有后续处理 如果是编译期异常 必须要通过throws/try{}处理
集合框架
集合框架 数组:保存同一类型的一列数据 长度固定 数组扩容 集合:保存不同类型数据(不建议) 长度不固定(自由增长和删除) 数据保存方案: 数组 连续 查询快 增删慢 链表 单链/双链 增删快 查询慢 树 二叉树 平衡树 红黑树 方便查找 栈 先进后出 队列 双口 先进先出 Collection接口(包含子接口 实现类中共有的方法)
泛型 为了防止集合中保存的数据类型不一致 导致后续使用中出现类型异常 java.lang.ClassCastException 所以加入泛型概念 实质:就是把运行时异常提升到编译期 案例1:Collection
泛型运用到类的设计中
当在设计类时 需要定义某些个具体的类型 可是设计的时候 还不能确定 可以用泛型替代
设计:public class Students
使用:Students
注意:定义时有几个泛型类别 在给真正的类型时 数量一定要一致
方法返回值使用泛型
(返回值并不方便在调用方法时赋泛型的类型 一般和参数的类型一致)
方法参数使用泛型
具备泛型的自定义类型
使用类中定义好的类型
public class Methods
//使用具备泛型的引用数据类型作为参数
public void show(Students<?,?> s) {
System.out.println(s.al.get(0));
}
public void show1(Collection
col = new ArrayList();
}
//直接使用类定义好的泛型 作为参数
public void show2(J j) {
System.out.println(j);
}
//返回值使用泛型<br /> public Collection<J> show5(ArrayList<J> a){<br /> Collection c = a;<br /> for(int i =0;i<a.size();i++) {<br /> c.add(a.get(i));<br /> }<br /> return c;<br /> }<br /> public J show3(J o) {<br /> J j = o;<br /> return j;<br /> }<br /> public L show4(L l) {<br /> return l;<br /> }<br />}
泛型用在接口中
- 接口中的泛型来源于类定义的泛型public class Students
implements StuInter - 接口中的泛型可以直接定义成具体的类public class Students
implements StuInter
不知道使用什么类型来定义或者接收的时候(抛开多态的运用) 可以使用泛型 泛型一定是给某个类的设计类的时候 可以使用泛型设计方法的时候 可以使用泛型定义泛型的语法 <大写字母>使用定义好的泛型 类上或者方法上已经定义好的泛型的大写字母
泛型通配符 ?(我理解:占座位)在过程中调用带泛型的类的时候(不是最终调用) 依然不确定类型的话 可以使用<?>代替类
最后所有设计过程中的泛型 在最终调用时一定要赋真正的类型(包装类)
通配符高级使用——-受限泛型 public void show1(Collection<? super Son> e) { //Collection可以存的类型为 Son的所有高级类 } public void show2(ArrayList<? extends Father> e) { //ArrayList可以存的类 型为 Father的所有后代类 } 练习: 已知Object类 String类 Number类 Integer类 其中Number类是Integer类的父类 要求创建四个类型的集合 创建方法 getElement1(Number类的子类集合) 创建方法 getElement2(Number类的父类集合) 测试类调用
练习2:
创建Student类 有两个不确定类型的成员变量 name age
创建StuUtil工具类
设计一个方法 展示某个学员的信息 (通过传入Student对象作为参数)
有一个存放学生对象的ArrayList集合 ConStu
提供了一个方法存储学生对象 通过传入一个Collection对象
测试类 测试
创建带有一个泛型L的StuInter接口 提供一个接收接口定义的泛型作为参数的showScore(L l)
Student去实现接口 重写方法 显示XXX的成绩是XX分
测试类
ArrayList
ArrayList实现类
无参构造: 默认开辟为10的数组
带参构造(int): 如果参数为0 依然走无参 如果>0 &&>10 扩容 扩容其1.5倍
grow()扩容方法
int oldCapacity = elementData.length; 保存老数组长度
int newCapacity = oldCapacity + (oldCapacity >> 1); 扩容
保存的最大值 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
boolean add(对象) 在集合末尾添加元素
void add(int index,对象) 在某个位置添加元素
boolean addAll(集合对象) 将参数中集合内的元素 追加到老集合的尾部
boolean addAll(int index,集合对象) 将参数中集合内的元素 添加进集合中
boolean removeAll(集合对象) 从列表中删除指定集合包含的所有元素
E get(int index) 获取索引为index的元素
Iterator
E remove(int index) 移除某个元素 返回这个元素
boolean remove(Objece o) 移除某个元素 返回布尔值
E set(int index, 对象) 用对象替换当前位置的对象
//普通for循环
for(int i =0;i
}
//增强for循环 底层就是迭代器
System.out.println();
for(String str : al) {
System.out.print(str+” “);
}
//迭代器
Iterator
//hasNext() 判断下一个元素是否存在 boolean
//next() 获取下一个元素
while(it.hasNext()) {
String str = it.next();
System.out.print(str+” “);
}
java.util.ConcurrentModificationException
解决迭代器使用过程中的并发访问异常
Iterator
//hasNext() 判断下一个元素是否存在 boolean
//next() 获取下一个元素
while(it.hasNext()) {
String str = it.next();
if(str.equals(“马六”)) {
it.remove();//在迭代过程中 如果要删除元素 要使用迭代器对象的remove()
continue;
}
System.out.print(str+” “);
}
//使用双向迭代器对象 实现逆向迭代展示ListIterator是Iterator的子类
List
ListIterator
//先使指针跳到最后位置
while(lis.hasNext()) {
lis.next();
}
//再逆向取值
while(lis.hasPrevious()) {
String str = lis.previous();
System.out.print(str+” “);
}
//线程安全的集合 效率低 最古老 默 认初始值10
Vector
vs.add(“张三”);
vs.add(“李四”);
vs.add(“王五”);
vs.add(“马六”);
Enumeration
while(es.hasMoreElements()) {
String str = es.nextElement();
System.out.print(str+” “);
}
LinkedList
LinkedList 双向链表 增删快 查询慢(从头开始)
一根链条 表示索引
一根链条 保存顺序
public class LinkedList
extends AbstractSequentialList
implements List
//构造方法 不带长度
public LinkedList() {
}
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
封装了一个链表实现 基于Node节点来实现数据存储关系
我利用数组进行查询的话 时间复杂度o(1) 链表形式查询 时间复杂度o(n)
LinkedList无默认长度
boollean add(元素对象) 【直接添加在链表的末尾 没有重新创建链表】
void add(int index,元素对象)
E get(int index)
boolean addAll(int index,Collection<? extends E> c)
void addFirst(元素对象)
void addLast(元素对象)
E getFirst()
E getLast()
int indextOf(元素对象)
int lastIndexOf(元素对象)
boolean offer(元素对象) 往尾部添加
boolean offerFirst(元素对象) 往头部添加
boolean offerLast(元素对象) 往尾部添加
E element() 查询但不删除首元素
E peek() 查询但不删除首元素
E peekFirst() 查询首元素
E peekLast() 查循尾元素
E poll() 查询并删除首元素
E pollFirst() 查询并删除首元素
E pollLast() 查询并删除尾元素
E remove(int index)
boolean remove(元素对象)
E removeFirst()
E removeLast()
E set(int index,元素对象) 替换当前位置的元素 返回被替换的元素对象
List集合 有索引 有序(储存添加的顺序和迭代展示的顺序一致)
&&&&& 如果我希望展示的顺序按照我的需求排列
基本数据类型 怎样排序
引用数据类型 怎么排序
Set集合 无索引 无序(储存添加的顺序和展示的顺序未必一致)
&&&&& 不能存储重复的数据
java中去重 除重依据:equals() hashCode() “通话” “重地”
当判断自定义类型的时候 可能内部的属性值是相同的 就认为是同一对象
但是此时 如果没有重写hashCode() 那么会获得两个不同的hash值 从而认为是两个不同的对象
Users u1 = new Users(“张三”,18);
Users u2 = new Users(“张三”,18);
equals() 不重写 false 重写 true
hashCode() 不重写 false 重写 true
当HashSet去存储这两个对象的时候 发现都返回的是true 就认为是同一个对象 只会保存一个
*结论 作为要去重的自定义数据类型 都要重写 equals() hashCode()
哈希值:十进制的一个整数 是有系统随机给出的(对象的地址)
hashCode()
每次展示对象的地址 末尾——>就是将hash值转成十六进制后的哈希值
HashSet集合实现类 (LinkedHashSet)
基本数据类型的存储 String类型 自动去重
自定义类型的存储 手动重写equals() hashCode()
LinkedHashSet底层多加了一根链条 用来保存存储顺序 所以迭代展示的时候 顺序和保存顺序一致
如果希望通过索引去获取Set实现类中保存的数据 那么必然需要把原集合保存在一个具备索引的集合中
ArrayList
比较器:
数组
Arrays.sort(基本数据类型的数组对象); 基本数据类型 默认升序排列
String类型 默认升序排列
对于保存了自定义类型的数组 会报错java.lang.ClassCastException 是因为没有比较器
解决方案:添加实体类中的比较器 实现比较规则
集合
List
Collections.sort(基本数据类型的集合对象); 默认升序排列
String类型 默认升序排列
自定义类型的List集合对象想要排序 添加比较器(两种方案都可以)
通过
Collections.sort(自定义类型集合对象); 或
Collections.sort(自定义类型集合对象,比较器对象);
Set
HashSet LinkedHashSet 比较器失效
创建比较器(两种方案皆可)
比较器一:
直接使用实体类去实现Comparable接口
public int compareTo(当前类的对象){
//比较规则
}
//在要比较的对象所属的类上面加比较器
public class Users implements Comparable
注意:
1.比较器不要选错了
2.加上泛型
@override
public int compareTo(当前类的对象){
//比较规则 并非算法
//先根据年龄排序
// if(this.age!=o.age) {
// 年龄是int类型 直接相减作为返回值
// return this.age-o.age;
// }else {
// //根据姓名排序 姓名是String类型 String类型提供了compareTo()方法比较
// return this.name.compareTo(o.name);
// }
}
}
使用
对于自定义类型的【数组】来说 直接使用以下方法 会自动调用排序规则
测试类中 Arrays.sort(数组对象);
对于【List集合】来说
测试类中 Collections.sort(集合对象);
比较器二:
当我们设计实体类的时候忘记了去实现Comparable接口 后续在代码扩展过程中需要完成比较功能
单独创建一个比较器类
public class UserCom implements Comparator
@Override
public int compare(Users o1, Users o2) {
// TODO Auto-generated method stub
//比较规则
if(o1.age!=o2.age) {
return o1.age-o2.age;
}else {
//根据姓名排序
return o1.name.compareTo(o2.name);
}
}
}
使用
在自定义类型的【数组】中比较
UserCom c = new UserCom();
Arrays.sort(数组对象, c);
在自定义类型的【List集合】中比较
Collections.sort(集合对象,比较器对象);
TreeSet对于所有数据都自动去重 前提是一定要有比较器 否则报java.lang.ClassCastException异常<br /> 使用第一种比较器方案 测试类中不需要任何操作 直接输出<br /> 使用第二种比较器方案<br /> 在TreeSet集合的构造方法的参数传入比较器对象<br /> TreeSet<User> hu = new TreeSet<User>(new UserCom());<br /> <br /> <br /> 去重<br /> List集合和数组 本身就可以保存重复数据<br /> TreeSet会自动去重 但是需要添加比较器 否则java.lang.ClassCastException<br /> HashSet LinkedHashSet 这两个集合需要手动添加equals() hashCode() 实现去重<br /> <br /> 排序<br /> 数组<br /> 基本数据类型和String 使用Arrays.sort(数组对象) 直接默认升序排列<br /> 自定义类型 要先设计比较器(两种方案) <br /> 再使用Arrays.sort(数组对象) Arrays.sort(数组对象,比较器对象) 进行按比较器规则排序<br /> List集合<br /> 基本数据类型和String 使用Collections.sort(集合对象) 直接默认升序排列<br /> 自定义类型 要先设计比较器(两种方案) <br /> 再使用Collections.sort(集合对象) Collections.sort(集合对象,比较器对象)<br /> Set集合<br /> HashSet LinkedHashSet 比较器无效 可以转成其他集合对象 再使用比较器<br /> TreeSet String类型和int类型都通过比较器去完成排序 比较器两种方案<br /> 比较器方案一 直接输出集合 不用做任何操作 <br /> 比较器方案二 在创建集合时的构造方法中添加比较器对象作为参数 输出<br /> 如果比较器中直接返回0 输出会直接返回第一个元素<br /> <br /> 优先使用 ArrayList LinkedList HashSet LinkedHashSet TreeSet<br /> 有序 有序 无序 有序 无序<br /> 底层数组 底层链表 哈希表+红黑树 哈希表+链表 底层二叉树<br />
HashMap<br /> <br /> 1.普通的方法<br /> public void show(){}<br />2.带基本数据类型的参数 或者返回基本数据类型作为返回值的方法<br /> public int show(String str){}<br />3.带自定义类型的参数 或者返回自定义类型作为返回值的方法 先创建自定义类型(成员变量 成员方法)<br /> public class User{ String name;}<br /> public User getUser(User u){} User u = new User(); User user = xxx.getUser(u); user.name<br />4.带基本数据类型的数组作为参数 返回基本数据类型的数组作为返回值的方法<br /> public int[] show(String[] str){}<br />5.带自定义类型的数组作为形参 返回值为自定义类型数组的方法<br /> //User[] us = new User[5];<br /> public User[] show(User[] u){}<br />6.带集合作为形参 集合作为返回值的方法<br /> public ArrayList<User> show(LinkedList<User> lu){}<br />7.利用多态 使用父类的引用作为方法的参数 或者方法的返回值<br /> public class User extends People{}<br /> public People show(People p){}<br />8.用父类作为集合类型 数组类型 来做参数或返回值<br /> public ArrayList<People> show(LinkedList<People> lp){}<br /> <br /> <br /> 双列集合Map<br /> 通过查看文档 该集合有两个泛型 一个泛型确定键的类型 一个泛型确定值的类型<br /> 是以键值对的映射的形式保存数据 键(类似于索引) 不能重复 值可以重复<br /> Map集合是独立于Collection的一种存储模式<br /> 实现类:<br /> HashMap :可以存储null 子类 :LinkedHashMap 存储取出有序<br /> HashTable(古老的集合散列集合) :不可以存储null 线程安全的 效率低<br /> <br /> HashMap:以哈希表的结构存储 存取顺序未必一致<br /> HashMap<String,V> map = new HashMap<String,V>();<br /> <br /> void .clear() 从这张地图中删除所有的映射。<br /> boolean .containsKey(键的对象) 如果此映射包含指定键的映射,则返回 true 。 <br /> boolean .containsValue(值) <br /> Set<Map.Entry<k,v>> .entrySet() 获取集合的所有键值对<br /> V .put(K key,V value)<br /> V .get(K key)<br /> Set<K> .keySet() 获取所有的键的集合<br /> Collection<V> .values() 获取所有的值的集合<br /> V .remove(K)<br /> boolean .remove(K,V)<br /> boolean .replace(K,oldValue,newValue) 替换指定键的条目。 <br /> int .size() 返回此地图中键值映射的数量 <br /> <br /> entry:键值对映射关系 (包含了键 值 包括对应的关系)<br /> 通过map.entrySet()获取这个对象<br /> .getKey()<br /> .getValue()<br /> <br /> map集合的遍历方案一:<br /> 通过entry键值对映射关系<br /> 遍历方案二:<br /> 先获取键的集合Set 对键的集合进行迭代<br /> 遍历方案三:<br /> 增强for循环 去遍历键的集合 <br />
面对对象复习:<br />代码规范性<br />会看代码
继承 至少是两个类 互相成就
类 对于对象的抽象
父类 对于类的抽象
public class Pet{
String name;
public Pet(){
}
public void play(){
}
}
public class Cat extends Pet{
public Cat(){
super();//调用父类构造方法 要放在子类构造方法的第一句话
}
public void getHao(){
super.play();
}
public void play(){
玩毛线
}
}
publci class Dog extends Pet{
public void lookMen(){
}
}
子父类关系确定后
super就可以使用
测试类中
Pet p = new Pet();
p.自己本类 .父类
Cat c = new Cat();
c.自己本类 .父类
Dog d = new Dog();
d.自己本类 .父类
Pet p = new Cat();
Pet p = new Dog();
p.自己本类 .父类
(1.子类重写了父类方法 调用执行的一定是这个重写之后的方法 如果没有重写 则调用父类自己的方法)
(2.子类中的方法 父类中没有(子类特有的方法) 通过p访问不到 会报错)
(3.如果想要调用特有方法 强转Cat c = (Cat)p;—->【Cat c = new Cat();】 c.任何本类信息 )
int l = (int)6.78;
多态
父类的壳里 可以存放任何子类的对象
public class Utile{
//展示狗的信息的方法
public void show(Dog d){
syso(d.toString());
}
//展示猫的信息的方法
public void show(Cat c){
syso(c.toString());
}
//多态运用在方法的参数中 形参放置为父类的壳 那么实参就可以是父类的任何子类对象
public void show1(Pet p){
}
}
比如 一个方法 不确定到底要接受猫还是狗作为参数 可以用父类的壳作为形参
class Test{
public static void main(String[] args){
Util u = new Util();
1.猫 2.狗<br /> if(i ==1){<br /> Cat c = new Cat();<br /> u.show1(c);<br /> <br /> }else if(i==2){<br /> Dog d = new Dog();<br /> u.show1(d);<br /> }
}
}
类 测试类(包含有main(String[] args))
public class Test{
//成员变量 private String name;
//所有的方法 应该在类中
public static void main(String[] args){
//所有逻辑代码 应该在方法中 其中可以有局部变量 public String bookName;
show();
Users u = new Users();<br /> u.showStu(); <br /> }
//主类中(包含main()的类)如果需要定义其他方法 一定要加上static
public static void show(){
}
}
//实体类
public class Users{
private String name;
public void showStu(){
addStu();
}
public void addStu(){
}
}
IO流
IO流
I input O out
File文件类 directory文件夹 path路径
对于文件和目录路经地址的一种抽象表现形式
创建文件、文件夹
删除文件、文件夹
获取文件、文件夹
判断是否存在
获取文件大小
- 在Window系统使用的是\反斜杠 反斜杠同时是转译符的标记 所以要再添加一根反斜杠才能标识是路径层级信息
File file = new File(“d:\aa\abc.txt”); “Class47_20/src/abc.txt”
File构造方法中的路经地址 不一定是真实存在的 可以不存在
通过参照物不同 路径地址有两种标识形式
相对路径 选择参照物定位文件的位置
绝对路径 是从根目录开始找到文件所在位置
../ 上跳一层
路径信息不分大小写 无论文件还是文件夹
File file = new File(“D:\aa\abc.txt”);
System.out.println(file);
static String pathSeparator ;
static char pathSeparatorChar ;
static String separator \
static char separatorChar \
System.out.println(File.pathSeparator);
System.out.println(File.pathSeparatorChar);
System.out.println(File.separator);
System.out.println(File.separatorChar);
File file = new File(“d:”+File.separator+”abc”);
System.out.println(file);
// File file1 = new File(“src\abc.txt”);
File file1 = new File(“src/abc.txt”);
System.out.println(file1.exists()); //当前路径是否正确
//System.out.println(file1.getAbsolutePath());
System.out.println(file.getName()); //获取文件名
System.out.println(file.getPath()); //获取路径地址
System.out.println(file.getAbsolutePath()); //绝对路径地址信息
System.out.println(file.length()); //文件内容大小 字节 如果不存在返回0
System.out.println(file.exists()); //文件是否存在 是true 否false
//File file2 = new File(“src/com/woniuxy/demo2/T.java”);
//System.out.println(file2.exists());
File file2 = new File(“D:\aa\ab.txt”);
System.out.println(file2.createNewFile());//创建文件
//File file3 = new File(“d:\abc”);
//file3.mkdir(); //创建文件夹
File file4 = new File(“d:\abc\ab”);
//file4.mkdirs(); //创建多级文件目录
System.out.println(file4.isDirectory());//判断是否是一个文件夹
System.out.println(file2.isFile());//判断是否是一个文件
System.out.println(file4.delete());
System.out.println(file4.delete());//需要把两个嵌套的文件夹都删除
//删除之后不走回收站 彻底删除
File[] files = file4.listFiles(); //获得文件类型(文件和文件夹)的数组
//展示多层级文件夹中的文件 (递归)
String[] strs = file4.list();//字符串那类型的数组
练习:
通过File类的方法 创建d:\abc\ab 内部添加有abc.txt abc.java abc.class abc.png aaa.java文件
输出所有文件的文件名
循环判断java文件的个数 并展示文件名
写出 读入
字节流 字符流
Writer Reader
OutputStream InputStream
数据的保存 临时记忆 内存
数据持久化 硬盘
内存——->硬盘 写出
硬盘——->内存 读入
硬盘—-内存—>硬盘 读入 写出
信息输出到控制台 读入的表现
内存—硬盘 写出 硬盘—内存 读入
字节流 OutputStream InputStream
字符流 Writer Reader
流:(字节 字符)1个字符=2个字节 = 16个bit
字节写出流OutputStream—>FileOutputStream
File file = new File(“D:\aa\ab.txt”); //创建写出目标地址对象
FileOutputStream fo = new FileOutputStream(file);//创建字节写出流对象 要把目标对象给他
write(byte[] b)
write(byte[] b,int start,int len)
write(int b)
//如果使用int类型做参数的方法
//0-127 会查询ASCII码
//超出了这个范围 会查询默认的一个码表(GBK)
//其他任何类型都要最终通过.getBytes()返回字节数组 执行正常的写出操作
byte[] b = new byte[] {97,98,99,100};
for(int i =0;i
fo.write(bb.toString().getBytes()); //转成byte[] 再去写出
}
fo.flush(); 刷新 为了将缓冲中的字节全部显示出
fo.close(); 关闭流
抛异常
在创建流对象时 可以添加第二个参数true/false 代表是否对前对象进行追加/覆盖
字节读入流InputStream—->FileInputStream
int read() 读取数据的下一个字节 并且自带下跳功能
int read(byte[] b) 可以从字节流中获取一些字节 存入准备好的缓冲区中
void close()
英文字母:
字节数 : 1;编码:GB2312
字节数 : 1;编码:GBK
字节数 : 1;编码:GB18030
字节数 : 1;编码:ISO-8859-1
字节数 : 1;编码:UTF-8
字节数 : 4;编码:UTF-16
字节数 : 2;编码:UTF-16BE
字节数 : 2;编码:UTF-16LE
中文汉字:
字节数 : 2;编码:GB2312
字节数 : 2;编码:GBK
字节数 : 2;编码:GB18030
字节数 : 1;编码:ISO-8859-1
字节数 : 3;编码:UTF-8
字节数 : 4;编码:UTF-16
字节数 : 2;编码:UTF-16BE
字节数 : 2;编码:UTF-16LE
练习:
d ab ggg.txt —-内存——> d abc aaa.txt
读入 写出
字节读入流 InputStream——>FileInputStream
FileInputStream fis = new FileInputStream(“d:\aa\ab.txt”);//从哪里读入
// int l = fis.read();//返回-1代表没有可读内容
// System.out.println((char)l);
// int l = 0;
// while((l=fis.read())!=-1) {
// System.out.println((char)l+”\t”+1);
// }
//d ab ggg.txt —-内存——> d abc aaa.txt
// 读入 写出
//采用缓冲区
byte[] b = new byte[1024];
//每次都读取缓冲区大小的字节数 读满了 就输出
//设置变量来保存每次读了多少字节
int l =0;
while((l=fis.read(b))!=-1) {
//使用字符串构造方法
//第一个参数为要连接元素的数组 第二个参数为起始下标 第三个参数为连接长度
System.out.println(new String(b,0,l));
}
fis.close();
字符写出流
Writer————>FileWriter
//使用字符流实现写出操作
/
FileWriter
write(int i) 一个字符
write(char[] c)
write(char[] c,int start,int length)
write(String str)
write(char c) 可以放入一个字符
/
FileWriter fw = new FileWriter(“d:\aa\ab.txt”,true);//写出的目标地址
// fw.write(‘段’);
// String str = “中国你好!中国你好!中国你好!”;
// fw.write(str);
//char[] c = new char[] {‘中’,’国’,’你’,’好’,’!’,’中’,’国’};
//fw.write(c);
//fw.write(“98”); //如果是int类型98 则输出b
fw.close();
字符读入流
Reader————->FileReader
//FileReader
/
int read()
int read(char[] c) //带缓冲区
close()
/
FileReader fr = new FileReader(“d:\aa\bb.txt”);//读取的文件来源
//int i = fr.read();
//System.out.println((char)i); //一次读一个字符
//读多字符 不带缓冲区
// int l =0;
// while((l=fr.read())!=-1) {
// System.out.print((char)l);
// }
//带缓冲区读取
int l =0;
char[] c = new char[1024];
while((l=fr.read(c))!=-1) {
System.out.println(new String(c,0,l));
}
fr.close();
使用io流复制一张图片
public class test{
public static void main(String[] args) thorws Exception{
File file =new File(D://a.jpg);复制的图片
BufferedInputStream bis=new BuffereInputStream(new FileInputStream(file))
File file=new File(D://b.jpg)
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(file1))
byte[] b=new byte[1024];
int len=0;
while((len=bis.read(b)!=-1))
bos.write(b,0,len)
}
bos.write
bis.write
}
&&&&总结:
写出:write()
字节:最终要转成字节数组.getBytes()
如果写一个字节 写一个数字 写一个字符串 都不用循环
如果要写出的是一个字节数组 则要循环把里面的每个元素取出 在转String 目的在于使用.getBytes()
字符:字符 字符串 字符数组
不需要循环 直接写
读入:read()
字节:没有缓冲区 则一个字节一个字节的读 不需要考虑读出非法数据的问题
有缓冲区 每次读缓冲区大小的字节 读满一次 输出一次 转String char 要考虑读出非法数据的问题
需要循环
字符:没有缓冲区 则一个字符一个字符的读 不需要考虑读出非法数据的问题
有缓冲区 每次读缓冲区大小的字符 读满一次 输出一次 转String char 要考虑读出非法数据的问题
需要循环
缓冲流:
字节
FileOutputStream BufferedOutputStream
FileInputStream BufferedInputStream
字符
FileWriter BufferedWriter
FileReader BufferedReader
FileInputStream fis = new FileInputStream(“d:\aa\2020_08_10.mp4”);
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream(“d:\abc\2020_08_10.mp4”);
BufferedOutputStream bos = new BufferedOutputStream(fos);
byte[] b = new byte[1024];
int l = 0;
while((l=bis.read(b))!=-1) {
bos.write(b, 0, l);
}
bos.close();
bis.close();
基本原理:是在创建流对象时,同时创建了一个默认大小的缓冲区数组,通过缓冲区的读写,减少IO次数 从而提高效率
字符缓冲流使用的方法有变化
read()——>readLine()
BufferedWriter 对象.newLine();
对象.write();
对象.write(“中国”);
对象.newLine();
对象.write(“你好”);
转换流 InputStreamReader OutputStreamWriter
FileInputStream fis = new FileInputStream(“d:\aa\ab.txt”);
InputStreamReader isr = new InputStreamReader(fis,”GBK”);
演示:
原文件与读入文件是同一字符编码集时 则能正常读入
改变原文件字符编码集 不能正常读入
不改变原代码的字符编码集 通过转换流设置要读入的文件的字符编码集 能正常读入
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(“”),”GBK”);
osw.writer(“”);
无论是字节流还是字符流 使用转换流进行读入时 缓冲区都设置为char[] c
序列化
目的:用一个字节序列表示一个对象 把程序中的某一个对象写出到文件中保存 这个过程叫做序列化
反之 这个字节序列还可以从文件中读入到内存中 重构对象 这个过程叫做反序列化
序列化:ObjectOutputStream类
** 要序列化对象的自定义类 一定要先设定可序列化 让这个类去实现 Serializable 接口
implements Serializable
完成序列化操作:
自定义类 对象名 = new 自定义类();
FileOutputStream fos = new FileOutputStream(“写出地址”);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(对象名);
oos.close();
fos.close();
反序列化:ObjectInputStream类
FileInputStream fis = new FileInputStream(“d:\aa\user1.txt”);
ObjectInputStream ois = new ObjectInputStream(fis);
Users u1 = (Users)ois.readObject(); //获取的是Object类型 要进行强转
ois.close();
fis.close();
System.out.println(u1);
u1.show();
1.序列化之后 如果对于原自定义类型代码进行了修改 那么反序列化会失败
java.io.InvalidClassException
解决:
在原类设计时 就添加一个来自于类实现的序列化接口内的一个静态常量serialVersionUID
private static final long serialVersionUID = 1L;//当前自定义类的版本号
那么此时重新进行序列化 把这个版本号也序列化存入字节序列中
再重复之前的修改操作 修改原类中的代码 例如 新增一个成员变量 保存
此时再次执行反序列化操作 不会报异常了(他是根据保存的版本号进行判断 是否是原自定义类型)
2.如果待序列化对象所属的类中 成员变量使用了static修饰 那么这个成员变量不会被序列化
(不报错)而是没有进入到字节序列中 所以反序列化后 当前成员变量值为默认值
如果使用transient关键字(瞬态关键字)修饰成员变量 那么同上
对于集合的序列化
ArrayList
au.add(new Users(“张三”,18));
au.add(new Users(“李四”,18));
au.add(new Users(“王五”,19));
au.add(new Users(“马六”,20));
FileOutputStream fos = new FileOutputStream(“d:\aa\user1.txt”);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(au);
oos.close();<br /> fos.close();<br /> <br /> //反序列化<br /> FileInputStream fis = new FileInputStream("d:\\aa\\user1.txt");<br /> <br /> ObjectInputStream ois = new ObjectInputStream(fis);<br /> <br /> ArrayList<Users> u1 = (ArrayList<Users>)ois.readObject();<br /> <br /> ois.close();<br /> fis.close();<br /> <br /> Iterator<Users> iu = u1.iterator();<br /> <br /> while(iu.hasNext()) {<br /> iu.next().show();<br /> }<br />File类<br />IO 四个基本流对象<br /> <br /> 读入 写出<br /> 字节流 字符流<br /> Writer Reader<br />OutputStream InputStream<br />** 缓冲流 四个高级流<br /> 字节 <br /> FileOutputStream BufferedOutputStream<br /> FileInputStream BufferedInputStream<br /> 字符<br /> FileWriter BufferedWriter<br /> FileReader BufferedReader<br /> 转换流 字节编码进行处理<br /> 转换流 InputStreamReader OutputStreamWriter<br />** 序列化流
复制图片
/
@ClassName: 复制图片.java
@Description: 复制图片
@version: v1.0.0
@author: 169986432
@date: 2020年10月20日 下午7:54:53
/
public class CopyFile {
public static void main(String[] args) throws Exception {
File oldFile=new File(“D:\22\1\work.txt”);
File newFile=new File(“D:\gogo\Haozip\2.txt”);
CopyFile copyFile =new CopyFile();
copyFile.copy(oldFile, newFile);
}
/
复制文件,有复制的旧文件,和新建的文件位置和名字
@throws Throwable
*/
public void copy(File oldFile,File newFile ) throws Exception {
//声明字符输入流对象
FileReader fr=null;
//声明字符输入流对象
BufferedReader br=null;
//声明字符输出流对象
FileWriter fw=null;
//声明字符输出缓冲流对象
BufferedWriter bw=null;
//创建字符输入流对象<br /> try {<br /> fr =new FileReader(oldFile);<br /> //创建字符输入缓冲流对象<br /> br=new BufferedReader(fr);<br /> //创建字符输出流对象<br /> fw=new FileWriter(newFile);
//创建字符输出缓冲流对象
bw=new BufferedWriter(fw);
//复制,一边读一边写
char[]c=new char[1024];
int lent;
while((lent=br.read(c))!=-1) {
bw.write(c,0,lent);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if(br!=null) {
br.close()
}
if(bw!=null) {
bw.close();
}
}
}
}
内部类
```
内部类
在一个类的内部 定义另外一个类
在一个类的内部的成员方法中定义一个类
方法的话 只能调用 不能嵌套 类 他可以嵌套
从设计角度来说 不建议使用内部类 因为内部类只是更加明确类与类之间的关系
从后期调用角度来说 他可以大幅度减少代码量 大幅度减少空间的利用
public class Classes{
class Students{
}
public void show(){
String name = “”;
class Message{
}
Message m = new Message();
m.
}
}
public protected (default) private
作为成员信息的内部类 public protected (default) private 都可修饰
调用外部类的信息 可以直接调用 语法:外部类的类名.this.成员变量、成员方法
调用本内部类的信息 this.
作为外部类成员方法中的内部类 不能写任何的访问修饰符 他也不是(default)默认情况 本类也不能访问
调用外部类的信息 可以直接调用 语法:外部类的类名.this.成员变量、成员方法
调用本内部类的信息 this.
调用所在成员方法中的局部变量 这个局部变量必须是常量 不能再次赋值
原因:
因为类的声明周期更长 一旦我的方法执行完出栈 那么方法中的这个局部变量就会消失 那类又去访问谁的值呢? 所以要求这个局部变量必须是一个值固定的常量 即使方法执行完 值依然可访问
特点:
1.从jdk1.8开始 包含内部类的成员方法中的局部变量 要么不被内部类所访问 可以随时用随时赋值
要么定义为常量 可以被内部类所访问 这种情况可以省略final关键字
2.如果该内部类中的成员变量和所在方法中的局部变量重名 那么访问的必然是本类中的变量(本类中的成员变量覆盖了外部方法中的局部变量)
调用:
作为成员信息的内部类的调用 语法:外部类对象.new 内部类类名()——>内部类对象
Classes cla = new Classes();
cla.new Students().showClasses();//通过创建匿名对象调用内部类信息
Students s = cla.new Students();//真实名称的对象 非匿名对象
System.out.println(new Classes().new Students().className);
System.out.println(s.className);
* 作为外部类成员方法中的内部类的调用
应该在该方法的内部创建这个内部类的对象 就可以进行相关调用了
public void show() {
String name = “张三”;//所在方法的成员变量 //默认的常量
//name = “李四”;
class Message{
String name=”李四”;
void showClasses() {
System.out.println(Classes.this.className);
System.out.println(name);
}
}
//应该在该方法的内部创建这个内部类的对象 就可以进行相关调用了
Message message = new Message();
message.showClasses();
}
内部类实际运用
没有实现类的接口实例的创建 内部类的运用
函数式编程—-》lambda表达式
public interface FunctionInter{
public void show();
}
public class User implements FunctionInter{
public void show(){
}
}
public class Student implements FunctionInter{
public void show(){
}
}
错误的 FunctionInter fi = new FunctionInter();
正确的 User u = new User();
FunctionInter fi1 = new User();
FunctionInter fi2 = new Student();
public class Utile{
public FunctionInter show1(FunctionInter f){
}
}
匿名内部类 省略了实现类/子类
匿名对象 省略了对象名 new 类型();
**定义接口
public interface FunctionInter {
public void show();
public int getNum();
}
&&&&方式一:没有实现类的情况下 对接口进行实现
FunctionInter fi = new FunctionInter() {
//匿名内部类 主要工作 实现接口中的方法
public void show() {
System.out.println(“使用内部类创建接口对象”);
}
public int getNum() {
return 1;
}
};
//接口的引用调用实现的方法
fi.show();
System.out.println(fi.getNum());
&&&&没有实现类的接口 去做方法的参数
public static void showInter(FunctionInter fi) {
fi.show();
System.out.println(fi.getNum());
}
main()方法中调用
//如果完成了方式一 可以直接将方式一中的fi作为实参传入
//Users fi = new Users();
//showInter(fi);
showInter(new FunctionInter() {
//接口的匿名内部类 来实现接口
@Override
public void show() {
// TODO Auto-generated method stub
System.out.println(“匿名内部类完成实参的接口传递”);
}
@Override<br /> public int getNum() {<br /> // TODO Auto-generated method stub<br /> return 8;<br /> } <br /> });
&&&&没有实现类的接口 作为方法的返回值
//设计一个没有实现类的接口 作为方法的返回值 的方法
public static FunctionInter getInter() {
//Users u = new Users();
FunctionInter fi = new FunctionInter() {
//匿名内部类
public void show() {
System.out.println(“使用内部类创建接口引用作为方法的返回值”);
}
public int getNum() {
return 10;
}
};
//最终返回一个接口的引用 在这之前 要对这个接口通过匿名内部类进行实现
return fi;
}
调用:
//接口的匿名内部类作为方法的返回值 准备一个接口的引用去接收这个方法
FunctionInter fff = getInter();
fff.show();
System.out.println((fff.getNum()+10));
//if(fff.getNum()==10){
//}
没有实现类的接口 内部有抽象方法 (是否有返回值 是否有参数)
情况一:方法内部
设计时 直接声明接口的引用指向 通过匿名内部类对于接口的实现(实现了内部抽象方法)
执行时 使用声明的接口的引用 调用接口的方法
设计方法中使用:
情况二:没有实现类的接口作为方法的参数
设计时 把形参(接口)当成是已知的实现类对象来使用
调用时 只有通过匿名内部类创建接口的实例作为参数传递
情况三:没有实现类的接口作为方法的返回值
设计时 只有通过匿名内部类创建接口的实例作为返回值
调用时 使用接口类型的引用接收方法 使用这个引用调用接口重写的方法
没有实现类的接口中 抽象方法的研究
如果抽象方法有返回值 —-》调用时 按照有返回值的方法操作即可
如果接口中的抽象方法有参数
情况一:实现抽象方法时添加形参 调用方法时给实参
情况二:设计接口作为参数的方法时 额外再添加一个参数 作为抽象方法的参数
情况三:设计方法中 对于接口进行实例化时 重写抽象方法时添加一个形参 最终调用这个方法时给实参
lambda表达式
目的简写接口【匿名内部类】 要求这个【接口】【只有一个唯一的抽象方法】 这样的接口叫做“函数式接口”
*明确是否是函数式接口 通过添加@FunctionalInterface
@FunctionalInterface
public interface Cook {
//提供了一个抽象方法
public void makeFood();
//public void show();
}
第一阶段 先不考虑抽象方法 参数 返回值
情况一:直接在方法中创建
Cook cook = ()->{System.out.println(“做了一桌满汉全席”);};
cook.makeFood();
情况二:函数式接口作为方法的参数
设计方法:
public static void showCook(Cook c) {
c.makeFood();
}
调用方法:
showCook(()->{System.out.println(“做了一桌满汉全席”);});
情况三:函数式接口作为方法的返回值
设计方法:
public static Cook showCook1() {
return ()->{System.out.println(“做了一桌满汉全席”);};
}
调用方法:
// Cook c = showCook1();
// c.makeFood();
showCook1().makeFood();
将所有的创建接口的实例化代码 换成了 lambda表达式
new Cook() {
//匿名内部类
public void makeFood() {
System.out.println(“做饭”);
}
}
替换为:
()->{System.out.println(“做饭”);};
第二阶段
抽象方法中 如果有返回值
情况一:
Cook cook = ()-> {return “做饭很开心” ;};
System.out.println(cook.makeFood());
情况二:
设计方法
public static void show1(Cook cook) {
System.out.println(cook.makeFood());
}
调用方法
show1(()->{return “做饭很开心”;});
情况三:
设计方法
public static Cook show2() {
return ()->{return “做饭很开心”;};
}
调用方法
System.out.println(show2().makeFood());
抽象方法中 如果有参数
情况一:
Cook cook = (String name)->{ System.out.println(“做了”+name);};
cook.makeFood(“小白菜”);
情况二:
设计方法
public static void show1(String name,Cook cook){
cook.makeFood(name);
}
调用方法
show1(“小白菜”,(String name)->{System.out.println(“做了”+name);});
情况三:
设计方法
public static Cook show2(){
return (String name)->{System.out.println(“做了”+name);};
}
调用方法
show2().makeFood(“小白菜”);
案例: 需要单独创建一个 Comparator接口的实现类 因为可以使用匿名内部类 所以这个类可以取消了
public class ComUsers implements Comparator
@Override
public int compare(Users o1, Users o2) {
// TODO Auto-generated method stub
return 0;
}
}
过时了
直接在要调用比较器的地方创建比较器接口的实例 (又因为此接口是函数式接口 还可以利用lambda表达式 三元运算符)
准备一个au集合
Collections.sort(au,(Users o1,Users o2)->{return (o1.age!=o2.age)?o1.age-o2.age:o1.name.compareTo(o2.name);});
迭代展示au集合
猜拳游戏
银行系统
快速敲击
五子棋
横 竖 左斜 右斜 判断输赢
随便给一个(x,y) 通过一个方法 返回焦点对象(离得最近的)
```
数据库
```
数据库
数据库服务器:安装了数据库软件的电脑
内存条 临时记忆 速度比较快 不能永久保存数据
硬盘 文件夹 文件 U盘 持久化数据信息 可以对于数据进行增删改查操作(能够同时操作大批量数据)
硬盘:存储数据
数据库软件: 1.数据持久化保存方案 2.数据库可以使大批量数据的操作更高效 3.需要购买
MySql SqlServer DB ..
可视化数据库软件:这个软件只是让我们更方便的去操作数据库,并不影响数据库的增删改查效率等
Navicat 等
数据库软件:存储数据的仓库 提供更丰富的数据操作功能 本质上就是一个文件系统 以此种方式存储到电脑的硬盘中
数据库软件分类: 主要类型:关系型数据库 表与表之间有深刻的依赖关系(范式)
Student{String name,int age}
classes{String claName,String teacher,ArrayList
Oracle 收费型数据库 性能高 服务额外收费 贵
MySql 开源免费数据库 Sun公司 Oracle收购 6.0以后开始收费
SqlServer 微软数据库 收费数据库
DB2 IBM公司提供数据库 收费的 银行系统
SQLite 嵌入式小型数据库 移动端 Android系统
java驱动
C#
DataBase(DB) 数据库
1.解压
默认三个数据库
mysql
performence_schema
test
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.管理员身份打开CMD
mysqld -install MySQL —defaults-file=”D:\soft\mysql-5.6.44-winx64\my-default.ini”
提示 successfully….
常见问题
1067 可能问题比较多
找不到路径 注册表的文件
差文件 C:\Windows\System32\msvcp100.dll 再安装驱动
路径不能有中文
第一个 检查配置文件 路径 不要空格
basedir=D:\soft\mysql-5.6.44-winx64
datadir=D:\soft\mysql-5.6.44-winx64\data
检查环境变量 必须设置正确D:\soft\mysql-5.6.44-winx64\bin
第二个 删除已有MySql文件 mysqld -remove
(防火墙暂时可关闭) 观察计算机管理界面的服务中是否还存在MySQL (刷新或者关闭后重新打开)
第三个 管理员身份打开路径到解压文件的bin目录下 cd bin
mysqld -install
显示成功后 net start MySql 看是否正常启动
启动成功后 直接通过CMD打开控制台
mysql -uroot -p(没有指定密码直接回车)
Enter password:(直接回车)
mysql>
再输入 show databases; 展示当前数据库中所有的数据库名称
初次登录没有密码想要设置密码 或者修改密码
打开CMD
mysqladmin -u root -p password 回车
Enter password: 输入老密码 没有密码就直接回车
New password:输入新密码
Confirm new password:重复输入新密码
使用新密码登陆
mysql -uroot -p回车
Enter password:输入新密码 回车
Welcome to the MySQL。。。
mysql>
退出登陆:exit 或者 quit 但是并没有关闭服务
开启服务:net start mysql
关闭服务:net stop mysql
SQL
Structured Query Language 结构化查询语句
一套标准 定义了所有关系型数据库的规则
(新建数据库 数据库的相关信息 表 标的设置 列 列的特性 内部数据的增删改查 等等)
SQL通用语法规则
可以单行或多行书写 但是一定要以;结尾
可以使用空格或缩进增强可读性
不区分大小写 建议关键字大写
单行注释 —【空格】注释内容
多行注释 /注释内容/
MySql方言:
#【空格】单行注释内容
分页查询有自己的语句
分类:四大类
1》DDL(Data Definition Language)数据定义语言
用来定义数据库对象:数据库 表 列 Create
2》DML(Data Manipulation Language)数据操作语言
对于数据的增删改 insert delete update
3》DQL(Data Query Language)数据操作语言
对于数据的查询 select
4》DCL(Data Control Language)数据控制语言
定义数据库访问权限 定义新的用户及权限 密码等等
Grant
DDL:CRUD create drop alter show
操作数据库
1.创建
创建数据库
create database 数据库名称;
先判断是否存在 不存在才创建
create database if not exists 数据库名称;
边创建边确定数据库编码集
create database 数据库名称 character set utf8;
create database if not exists 数据库名称 character set utf8;
2.查询
show databases;查询所有的数据库
show create database 数据库名称; 查询某个数据库的编码集
3.修改
alter database 数据库名称 character set 新的字符编码集;
修改原有数据库的字符编码集 (在录入数据前改好)
4.删除
drop database 数据库名称;
删除数据可
drop database if exists 数据库名称;
先判断是否存在 再删除
5.使用数据库
use 数据库名称;
操作数据之前 先确定数据库
select database();
查询当前使用的数据库名
操作表
6.创建表(同时应该创建列)
create table 表名(列名1 数据类型1,列名2 数据类型2,.。。);
如果定义varchar 就一定要在后面小括号中 定义长度 varchar(255)
int、double、date(yyyy-MM-dd)、dateTime(yyyy-MM-dd HH:mm:ss)
char、float
复制一张表
create table 新表名 like 旧表名;
7.查询表
show tables;
查询当前数据库中所有的表
desc 表名;
查询当前表的结构
8.修改
alter table 表名 rename to 新表名;
修改表名
alter table 表名 add 列名 类型;
添加一列
alter table 表名 change 列名 新列名 新类型;
修改列名
alter table 表名 modify 列名 新类型;
修改列的类型
9.删除
alter table 表名 drop 列名;
删除表中列
drop table 表名;
删除表
drop table if exists 表名;
先判断 再删除
DML:数据的增删改操作 insert into delete update
1.添加数据
insert into 表名(列名1,列名2,。。。) values (值1,值2,。。。);
注意:
列名要和值一一对应
除了形式为数字的值 其他都应该加’’或者””
如果整行添加 可以省略列名 inster into 表名 values(值1,值2,。。。);
如果有自增列
*insert into 表名 values(null,值2,值3,。。);
insert into 表名 (列名2,列名3,。。。) values(值2,值3,。。。);
2.删除操作
delete from 表名;
删除整张表
delete from 表名 [where goodName = “笔记本”];
注意:
做删除操作一定要编写删除条件 否则整表删除
如果确实要删除整表信息 truncate table 表名; 删除整表 重新复制一张原表结构 效率高
delete from 表名 where goodName = “笔记本” or id = 1;
多行删除
3.修改数据
update 表名 set 列名 = 值;
修改所有的行此列的值
update 表名 set 列名 = 值 where 条件;
update goods set goodName = “书包” where goodName = “文具盒”;
同时改变多行中某一列的值 可以在条件中添加多行的查询
update goods set goodName = “书包” where goodName = “文具盒” or id = 3;
同时改变多列的值
update goods set goodName = “书包”,price = 20.8 where…
批量新增
INSERT INTO
goods
VALUES
(2,’钢笔’,5,1)
,(3,’作业本’,0.5,1)
,(4,’文具盒’,10.6,1)
,(5,’篮球’,58.8,2)
,(6,’羽毛球’,3,2)
,(7,’羽毛球拍’,80,2)
,(8,’面包’,5,3)
,(9,’牛奶’,4.5,3)
,(10,’辣条’,0.5,3);
DQL:数据的查询操作 select
select from 表名;
查询整张表
select id,goodName,price from 表名;
查询表中的某些列
select 列1,列2,列3,…from 表名 [where 条件];
查询满足条件的行中的某些列
— 查询中去除列中重复的数据
SELECT DISTINCT type FROM goods;
使用去重查询 不应该再和其他列一起操作
— 如果希望以某个值代替price列中的null
SELECT
id as ID,goodName [as] 商品名,IFNULL(price,0)+2 as 价钱
FROM
goods;
基本的运算
只能用于数字类型列
如果运算中出现了null 那么结果就是null
如果不小心用到了关键字 key
(不是单引号)
where子句
运算符 > < >= <= != <> = is null
and && or || 【between .. and ..】 in 【not in】
like 执行模糊查询 _ 代表一个字符 %代表0-n个字符
区间查找
SELECT FROM goods WHERE price<=5 AND price>=0;
其中and 可以替换为 &&
等同于
SELECT FROM goods WHERE price BETWEEN 0 AND 5;
其中and 不可以替换为 &&
SELECT FROM
goods
WHERE
goodName = ‘铅笔’
OR
goodName = ‘羽毛球’
OR
goodName = ‘羽毛球拍’;
其中 or 可以换为 ||
等同于
SELECT FROM goods WHERE goodName in (‘铅笔’,’羽毛球’,’羽毛球拍’,’电脑’);
排序操作
order by 排序列 默认升序排列 (ASC) 降序 DESC
SELECT FROM goods ORDER BY price,id DESC;
注意:
order by 永远在语句的最后面 代表对于虚拟表进行排序
聚合函数
count() 求行数 不推荐放 随便放一个列名 count(id)
sum(列名) 对于列求和
avg(列名) 对于列求平均数
max(列名) 求本列中最大值
min(列名) 求本列中最小值
注意: 查询聚合函数时 一般不查询普通列
分组查询
group by 分组列
— 分组查询
SELECT type FROM goods GROUP BY type;
— 展示每一组的总价
SELECT type ,SUM(price) FROM goods GROUP BY type;
**当有分组的情况下 可以查询 分组列 聚合函数列
— 排序
SELECT type ,SUM(price) as 总价 FROM goods GROUP BY type ORDER BY 总价;
— where where在分组前对于真实表进行第一次筛查
SELECT type ,SUM(price) as 总价 FROM goods WHERE price<5 GROUP BY type ORDER BY 总价 DESC;
— having 对于分组后的查询结果继续筛查
SELECT type ,SUM(price) as 总价 FROM goods WHERE price<5 GROUP BY type HAVING 总价 >2 ORDER BY 总价 DESC;
select
分组列 聚合函数列
from
表名
where
条件子句
group by
分组列
having
条件子句
order by
排序列 ASC/DESC
limit
当前页的首行下标 = (当前页码-1)每页的行数
&&&& where和having
都是条件筛选
1.
where在分组之前对于真实表进行筛选 如果不满足条件则不参与分组
having在分组之后对于虚拟表进行筛选 如果不满足条件不展示
2.
where后不可以跟聚合函数
having后可以跟聚合函数进行并判断
3.
where操作的是真实表
having操作的是虚拟表
分页查询
目的:当要展示的数据信息很庞大时 建议根据每页显示的条数来查询固定条数的信息 通过上一页 下一页 第几页这样的按钮执行更多数据的查询(每一次都要查询)
limit 当前页的首行0,每页的行数
明确的信息:
每页展示几行
总共需要展示多少行 —-》需要多少页
当前页码
当前页的首行下标 = (当前页码-1)每页的行数
SELECT FROM goods LIMIT 12,4;
# 创建数据库 设置字符编码集
CREATE DATABASE IF NOT EXISTS shop CHARACTER SET utf8;
— 选择使用某个数据库
USE shop;
— 创建表
CREATE TABLE goods (id INT,goodName VARCHAR(255),price DOUBLE,type INT);
CREATE TABLE types (id INT,typeName VARCHAR(255));
— 插入数据
INSERT INTO goods VALUES(1,’铅笔’,0.8,1);
INSERT INTO
types
VALUES
(1,’文具类’)
,(2,’运动类’)
,(3,’食品类’)
,(4,’服装类’)
,(5,’美妆类’);
INSERT INTO
goods
VALUES
(2,’钢笔’,5,1)
,(3,’作业本’,0.5,1)
,(4,’文具盒’,10.6,1)
,(5,’篮球’,58.8,2)
,(6,’羽毛球’,3,2)
,(7,’羽毛球拍’,80,2)
,(8,’面包’,5,3)
,(9,’牛奶’,4.5,3)
,(10,’辣条’,0.5,3);
INSERT INTO goods VALUES(11,’羽毛球拍’,10,2);
INSERT INTO goods VALUES(13,’羽毛球拍’,55,2);
UPDATE goods SET goodName = ‘爽肤水’,type = 4 WHERE id=12;
SELECT FROM goods;
SELECT FROM types;
SELECT id,goodName FROM goods;
SELECT
type,price
FROM
goods
WHERE
goodName = ‘羽毛球’
OR
goodName = ‘羽毛球拍’;
— 查询中去除列中重复的数据
SELECT DISTINCT type FROM goods;
SELECT
id,goodName,price+2,type
FROM
goods;
— 如果希望以某个值代替null
SELECT
id as ID,goodName 商品名,IFNULL(price,0)+2 as 价钱
FROM
goods;
SELECT * FROM goods WHERE goodName <> ‘羽毛球拍’;
SELECT * FROM goods WHERE price<=5 && price>=0;
SELECT * FROM goods WHERE price BETWEEN 0 AND 5;
SELECT * FROM
goods
WHERE
goodName = ‘铅笔’
||
goodName = ‘羽毛球’
OR
goodName = ‘羽毛球拍’;
SELECT * FROM goods WHERE goodName NOT in (‘铅笔’,’羽毛球’,’羽毛球拍’,’电脑’);
SELECT * FROM goods WHERE price IS NULL;
SELECT * FROM goods WHERE goodName LIKE ‘羽毛球拍%’;
SELECT * FROM goods ORDER BY price,id DESC; — ASC;
SELECT * FROM goods WHERE goodName = ‘羽毛球拍’ ORDER BY price DESC;
SELECT SUM(price) AS 总价 FROM goods WHERE id BETWEEN 1 AND 5;
SELECT COUNT(id) AS 商品数目 FROM goods;
— 分组查询
SELECT type FROM goods GROUP BY type;
— 展示每一组的总价
SELECT type ,SUM(price) as 总价 FROM goods GROUP BY type ORDER BY 总价;
SELECT type ,SUM(price) as 总价 FROM goods WHERE price<5 GROUP BY type HAVING 总价 >2 ORDER BY 总价 DESC;
SELECT * FROM goods LIMIT 12,4;
约束
约束
概念:对表中的数据进行限定,保证数据的正确性、有效性、完整性。
分类:
主键约束+自增列
非空约束
唯一约束
外键约束
主键约束:
给表中每一行添加唯一标识 一般设为int类型 进行自增
create table 表名(
id int primary key — 设置id列为本表的主键列
,name varchar(255) — 姓名
,stuType int — 专业
)
效果:不允许重复 不能为null 非空且唯一
删除表中的主键列
ALTER TABLE students DROP PRIMARY KEY;
在表创建后设置主键列 设置前 要保证内部已有数据是符合要求的
ALTER TABLE students MODIFY stuId int PRIMARY KEY;
添加主键 设置自增 自增列只能添加在主键上
&&&& create table 表名(
id int primary key auto_increament — 将主键设为自增
,name varchar(255) — 姓名
,stuType int — 专业
)
单独添加自增
ALTER TABLE students MODIFY stuId int auto_increment;
有主键自增的表添加数据 自增列的值用NULL代替
INSERT INTO
students
VALUES(NULL,’王五’,1);
删除自增
ALTER TABLE students MODIFY stuId int;
非空约束:
在设计表时 给表中某些列设置非空约束
create table 表名(
id int primary key — 设置id列为本表的主键列
,name varchar(255) not null — 姓名 设置非空约束
,stuType int — 专业
)
效果:不允许重复 不能为null 非空且唯一
单独添加非空约束
ALTER TABLE students MODIFY stuName VARCHAR(255) NOT NULL;
删除非空约束
ALTER TABLE students MODIFY stuName VARCHAR(255);
唯一约束:
在设计表时 给表中某些列设置唯一约束
create table 表名(
id int primary key — 设置id列为本表的主键列
,name varchar(255) not null — 姓名 设置非空约束
,tel varchar(255) unique — 电话号码 设置唯一约束
,stuType int — 专业
)
单独添加唯一约束
ALTER TABLE students MODIFY stuTel VARCHAR(255) UNIQUE;
ALTER TABLE students ADD UNIQUE(stuTel);
删除唯一约束 通过删除这个列的索引(index)来删除唯一约束
ALTER TABLE students DROP INDEX stuTel;
— ALTER TABLE students MODIFY stuTel VARCHAR(255); — 错误方式
外键约束:
至少涉及到两张表 描述表与表之间列的取值的依赖关系
设置 从表 的某一列的外键 指向 主表的主键列
create table 表名(
id int primary key — 设置id列为本表的主键列
,name varchar(255) not null — 姓名 设置非空约束
,tel varchar(255) unique — 电话号码 设置唯一约束
,stuType int — 专业
,CONSTRAINT 外键名称 FOREIGN KEY(从表列) REFERENCES 主表(主键列);
)
— 设置从表的某个列的外键
ALTER TABLE students ADD CONSTRAINT type【外键名称】 FOREIGN KEY(stuType) REFERENCES classType(classId);
— 删除外键
ALTER TABLE students DROP FOREIGN KEY 外键名称;
数据库的设计
数据库的设计
```
数据库的设计
根据需求设计数据库 思考功能
有了数据库 其实就有了实体类 创建实体类 entity包
根据功能设计接口 数据库连接类 dao包
依次实现接口 daoImp包
方法的逻辑连接(方法) utils包
测试类 test包
表与表之间的关系 外键约束
一对一
一对一的情况一般建议描述在一张表中
例如:学生和学号 人和身份证
如何描述一对一的关系:
任意一方添加外键约束 指向另一方的主键
(无所谓方向)
一对多
建议拆成两张或更多表
例如:学生(多)对班级(一) 人(多)对家庭住址(一)
给多表的列添加外键 指向一表的主键列(必须有方向性)
* 多对多
建议添加第三张中间表
例如:玩家(多)对 游戏(多)
添加一张表 这张表至少包含两个列 这两个列分别添加外键 指向另外两张多表的主键
(演示)
如何使用设计软件来添加外键关系
选择从表右键选择设计 在上方选择外键
1.起个外键名称 2.当前从表的要做主外键的列 3.数据库的名称 4.主表名 5.主表主键 后面是否级联
范式:(设计多张表)
概念:设计数据库时 应该遵循的一些规范 要遵循后面的范式要求 就必须遵循前面的所有范式要求 各种范式呈递次规范 满足越高的范式的数据库 冗余越小 越优秀
目前数据库遵循六大范式:第一范式1NF 第二范式2NF 第三范式3NF 巴斯-科德范式BCNF 第四范式4NF 第五范式5NF 123845
第一范式1NF:(各列不可再拆分)
数据库表的每一列都是不可分割的原子数据项 而不能是集合 数组 记录等非原子数据项
第二范式2NF:(每张表应该选择一个合适的主键)
非码属性必须完全依赖于候选码(主键)(在1NF基础上消除非主属性对主码的部分函数依赖)
间接依赖到主键 满足二范式
第三范式3NF:(其他列能够直接依赖于主键)
任何非主属性不依赖于其他的非主属性(其他列只依赖于主键 包括间接依赖都不行)(在1NF和2NF基础上)
第三范式就是属性不依赖于其它非主属性,也就是在满足2NF的基础上,任何非主属性不得传递依赖于主属性。
事务:一次要执行的一系列操作的总称
通过添加事务 可以控制这一系列操作的成功或失败 有一个sql失败则全部还原到开启事务之前 只有全部的sql成功 才会正常提交(提交还是回滚的主动权在程序员手里)
语法:
START TRANSACTION;开启事务(在数据库编码中)
事务中的一系列操作
如果其中的操作都符合需求 则 可以使用COMMIT; 真正的去正常提交更新
如果其中的操作有不符合需求的 则可以使用ROLLBACK;使数据回滚到开启事务之初。
事务提交的两种方式:
自动提交:MySql 每条SQL就是一个事务 默认开启和提交
手动提交:Oracle 需要显式开启事务和提交事务
查询事务提交方式:
select @@autocommit;
1:自动提交
0:手动提交
SET @@autocommit =0;— 设置提交方式为手动提交
SELECT @@autocommit;— 查询提交方式
验证:直接执行修改语句 会发现数据库中数据并没有被修改
只能通过添加事务及提交操作 才能实现修改
自动提交的SQL 只需要添加事务及提交事务 即可变为手动提交 不需要单独修改提交方式
事务四大特性:
1.原子性:我们定义好的一个事务 就应该是不可分割的最小操作单位 要么同时成功 要么同时失败
2.持久性:只有当事务执行了commit或者rollback后 数据才会持久的保存在数据库中
3.隔离性:事务与事务之间是相互独立的(理想状态)
4.一致性:事务操作前后 数据总量是不变的 1》不可重复读 2》数据完整性
读取异常:查询 当一方开启事务操作数据后 没有提交或者回滚之前 查询到的数据和真实数据是不同的 这就造成了读取异常。
1.脏读:一个事务读取到另一个事务还没有保存的数据
2.不可重复读:在同一个事务中两次读取到的信息不一致
3.幻读:一个事务在操作增删改(DML)数据时 另一个事务添加了一条数据 则第一个事务查询不到自己的修改 出现幻觉
事务隔离级别:
1.read uncommited 读未提交
问题:脏读、不可重复读、幻读
2.read commited 读已提交 (oracle默认隔离级别)
问题:不可重复读、幻读
3.repeatable read 可重复读 (MySql默认隔离级别)
问题:幻读 (暂不演示)
4.serializable 串行化
解决以上所有问题 暂停其他事务 效率最低
查看隔离级别
select @@tx_isolation;
设置隔离级别
set global transaction isolation level 隔离级别名称;
修改完隔离级别后 要重新创建连接
验证一:
将MySql事务隔离级别降低到 读未提交
在一个查询中新建一个事务 更新数据的sql 执行 但是不提交
在另一个查询中新建一个事务 查询这个数据 发现读取到了一个假的信息 未提交的数据
当第一个事务执行了rollback后 第二个事务再次查询 发现数据变为原始数据
验证二:
重新实现一的操作 发现在第一个事务没有提交或者回滚的情况下 第二个事务查询到的数据都是原始数据 不会读到没有更新的数据
会出现不可重复读的情况:在第一个事务去更新数据 但是不提交 通过第二个事务去查询(这个事务要手动开启 而且不提交 保证下次查询依然是这同一个事务) 发现读取到的是原始数据(排除了脏读)
再将第一个事务进行正常的提交 commit 这时再来第二个事务只执行查询语句(不要重新开启事务 他会默认为刚才那次事务的第二个操作) 此时会发现读到了更新后的数据(和第一次读到的数据不同 不可重复读)
验证三:
将隔离级别提升为可重复读
在第一个事务去更新数据 但是不提交 通过第二个事务去查询(这个事务要手动开启 而且不提交 保证下次查询依然是这同一个事务) 发现读取到的是原始数据(排除了脏读)
再将第一个事务进行正常的提交 commit 这时再来第二个事务只执行查询语句(不要重新开启事务 他会默认为刚才那次事务的第二个操作) 此时会发现读到的数据依然是第一次查询到的数据(虽然数据库其实已经变化了) 这个叫做可重复读 一旦这个事务执行了提交 再次读取就会读到真正的数据
验证四:
设置隔离级别为最高级 重新实现验证一的操作 发现当第一个事务没有提交或回滚前 其他事务都处于等待状态 直到第一个事务提交或者回滚后 等待的事务自动执行 等待事务有个最长等待时间 超出报错
锁:
悲观锁
事务中的业务大概率会出现并发异常时 我们处于一种悲观状态 可以使用悲观锁
先关闭默认提交模式
SET @@autocommit =0;— 设置提交方式为手动提交
START TRANSACTION;
— 加锁
select money from Users
where id = 1 for update;
update Users
set money =50000 where id = 1 ;
— 在关闭这个事务之前 其他的事务依然查询的是数据库中真实的数据 (没有脏读)
— 对比事务隔离级别serializable 在第二个事务中查询也会被暂停 而使用悲观锁 第二个事务是可以查询的
— 如果其他事务使用的是DML(增删改)操作 会被暂停 出于等待 直到第一个事务提交或者回滚 其他事务才能进行(DML)
乐观锁
评估事务中发生并发异常的可能性比较低 但又不完全保证不会发生 使用乐观锁
乐观锁是通过SQL本身的语法去进行校验
— 在修改之前查询一下数据 在修改的同时回去判断这个值和刚才查询到的数据是否一致 如果一致则继续执行更新 否则不更新
SELECT MONEY FROM USERS WHERE ID = 1;
UPDATE USERS SET MONEY = 500 WHERE ID = 1 AND MONTY = 前面语句查出来的值;
合成一句
UPDATE Users
SET money = 500
WHERE id = 1
AND Users
.money = (SELECT m1 FROM (SELECT money AS m1 FROM Users
WHERE id = 1 ) AS t1);
Every derived table must have its own alias 给虚拟表命名
DCL:对于数据库用户权限的相关设置
查询数据库用户:
USE mysql;
SELECT * FROM user
;
新增用户:
— 新增数据库管理
CREATE USER ‘用户名’@’localhost’ IDENTIFIED BY ‘密码’;
修改密码
UPDATE USER SET PASSWORD = PASSWORD(‘333’) WHERE USER = ‘abc’;
FLUSH PRIVILEGES; — 刷新操作
— 简化方案
SET PASSWORD FOR ‘abc’@’localhost’ = PASSWORD(‘1234567’);
当遗忘了root密码 如何重置root密码
1.使用管理员方式打开CMD-> net stop mysql 停止mysql服务
2.使用无验证方式登入mysql mysqld —skip-grant-tables
3.打开新的cmd窗口 直接输入mysql 回车 登入mysql mysql>
4.use mysql;
5.UPDATE USER SET PASSWORD = PASSWORD(‘新密码’) WHERE USER = ‘root’;
6.关闭两个窗口
7.开启服务 net start mysql
8.使用root及新密码登陆
删除用户
— 删除用户
DROP USER ‘用户名’@’localhost’;
权限管理:
查询权限
— 查询权限
SHOW GRANTS FOR ‘用户名’@’localhost’;
— 列表式赋权限
GRANT SELECT,UPDATE,INSERT,DELETE ON test1.users TO ‘abc’@’localhost’;
— 列表式移除权限
REVOKE SELECT,UPDATE,INSERT,DELETE ON test1.users FROM ‘abc’@’localhost’;
-- 拥有所有权限<br /> GRANT ALL ON *.* TO 'abc'@'localhost';<br /> -- 删除所有权限<br /> REVOKE ALL ON *.* FROM 'abc'@'localhost';<br /> <br /> ** grant引导赋权限的语句 revoke引导删除权限语句<br /> ** 使用列表时赋予的权限 只能使用列表式删除 使用all赋予的权限 只能使用all删除<br /> <br /> <br /> end
JDBC链接数据库:
```
JDBC
java database connectivity java连接数据库技术
本质:java官方为了实现访问和操作各个数据库 提供的一套标准 即接口
接口的实现:由各个数据库厂商自己去实现这套接口 实现类都会被封装在各自的jar包中
具体操作:
1.导入jar包
2.加载驱动
3.获取连接对象(数据库 用户名 密码)
4.创建statement对象 去执行SQL语句
5.增删改会返回受影响的行数 int 查询会返回查询的结果集result对象
6.先关闭结果集 关闭statement对象 关闭连接
connection对象:数据库连接对象 由驱动创建
Statement对象:执行sql的对象 由连接对象创建
加载驱动
Class.forName(“com.mysql.jdbc.Driver”);
获取连接
conn = DriverManager.getConnection(“jdbc:mysql://localhost:3306/Petsys?useUnicode=true&characterEncoding=utf-8”, “root”, “”);
预备SQL:
String sql = “INSERT INTO pet VALUES(null,’”+p.name+”‘,”+p.typoe+”,”+p.sex+”,’”+p.date+”‘,’”+p.des+”‘)”;
创建处理SQL语句的对象
Statement ps = conn.createStatement();
传入SQL并且执行对应的方法
int l = ps.executeUpdate(sql); 增删改
ResultSet rs = ps.executeQuery(sql); 查询
关闭连接
rs.close();
ps.close();
conn.close();
问题一:
连接SQL语句很麻烦 双引号单引号来回嵌套 容易出错
问题二:
每一次都需要重构完整的sql语句 降低了操作效率
问题三:
安全问题
String sql = “select from user where username= ‘“+varname+”‘ and userpwd=’”+varpasswd+”‘“;
把’or ‘1’ = ‘1当作密码传进去 会组合成如下sql
select from user where username = ‘user’ and userpwd = ‘’ or ‘1’ = ‘1’;
出现在并没有查询到正确匹配的用户名和密码的情况下 查到了数据
优化:
目标 完成petsys数据库中pet表的增删改查
1.数据库连接类BaseDao
加载驱动
创建连接的方法
//获取连接对象
public Connection getConn() throws Exception {
//加载驱动
Class.forName(driver);
//获取连接对象
this.conn = DriverManager.getConnection(url, user, pwd);
return conn;
}
关闭各种资源的方法
// 关闭所有对象
public void closeAll(Connection conn,Statement ss, ResultSet rs) throws Exception {
if(rs!=null) {
rs.close();
}
if(ss!=null) {
ss.close();
}
if(conn!=null) {
conn.close();
}
}
增删改共用方法
//增删改共用方法
public int excUp(String sql,Object[] obj) {
int l =0;
try {
//获取连接
this.conn = getConn();
//预编译
this.ps = this.conn.prepareStatement(sql);
//赋值
for(int i =0;i
}
//执行增删改
l = ps.executeUpdate();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
try {
closeAll(conn, ps, null);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return l;
}
2.实体类
3.接口类
4.实现接口类
实现增删改
public int delPet(String petName) {
// TODO Auto-generated method stub
//准备sql语句
String sql = “delete from pet where petname = ?”;
//准备要补充sql语句的值
Object[] obj = new Object[] {petName};
//调用BaseDao中增删改共用方法
int l = bd.excUp(sql, obj);
return l;
}
实现查询
public ArrayList
// TODO Auto-generated method stub
ArrayList
String sql = "SELECT petname,breads.breads,sexs.sex,pet.Birthday,pet.Description\r\n" + <br /> "FROM pet,breads,sexs\r\n" + <br /> "WHERE pet.PetBreed = breads.id \r\n" + <br /> "AND pet.PetSex = sexs.id;";<br /> Connection conn = null;<br /> PreparedStatement ps = null;<br /> ResultSet rs = null;
try {<br /> //获取连接<br /> conn = bd.getConn();<br /> //预编译<br /> ps = conn.prepareStatement(sql);<br /> <br /> //如果查询语句中有多个?<br /> // Object[] obj = new Object[] {petName};<br /> //for(int i =0;i<obj.length;i++) {<br /> // ps.setObject(i+1, obj[i]);<br /> //}<br /> //如果查询语句中只有一个?<br /> //ps.setObject(值)<br /> <br /> //执行查询<br /> rs = ps.executeQuery();<br /> <br /> //rs.next()获取并判断结果集中下一个值是否存在 自动下跳<br /> while(rs.next()) {<br /> //把每一个rs保存进一个pet对象中<br /> //把这个pet对象保存进集合中<br /> Pet p = new Pet();
//结果集.get类型(查询出的虚拟表的第几列 从1开始)<br /> p.setPetName(rs.getString(1));<br /> p.setPetBreed(rs.getString(2));<br /> p.setPetSex(rs.getString(3).charAt(0));<br /> p.setBirthday(rs.getString(4));<br /> p.setDes(rs.getString(5));
ap.add(p);<br /> }<br /> } catch (Exception e) {<br /> // TODO Auto-generated catch block<br /> e.printStackTrace();<br /> }finally {<br /> try {<br /> bd.closeAll(conn, ps, rs);<br /> } catch (Exception e) {<br /> // TODO Auto-generated catch block<br /> e.printStackTrace();<br /> }<br /> }<br /> return ap;<br /> }<br /> <br /> <br /> <br />以配置文件的方式配置数据库连接信息<br />使用xxx.properties文件来保存信息<br />BaseDao中去读取文件信息
配置文件信息:xxx.properties 放置在src文件下
url=jdbc:mysql://localhost:3306/petsys?useUnicode=true&characterEncoding=utf-8
user=root
password=
driver=com.mysql.jdbc.Driver
class BaseDao{
//驱动类
private static String driver ;
private static String url ;
private static String user ;
private static String pwd ;
static {
//配置文件读取并赋值
//使用Properties集合类
//通过当前这个类的字节码文件 找到类加载器classLoader
//目的:找到配置文件的绝对路径 path
ClassLoader classLoader = BaseDao.class.getClassLoader();
URL u = classLoader.getResource(“jdbc.properties”);
String path = u.getPath();
//System.out.println(path);
// 使用集合类 集合框架中的一个类 List Map Properties
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();
}
// try {
// pro.load(new FileInputStream(path));
// } catch (FileNotFoundException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// } catch (IOException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
//获取配置文件信息
driver = pro.getProperty(“driver”);
//System.out.println(driver);
url = pro.getProperty(“url”);
//System.out.println(url);
user = pro.getProperty(“user”);
pwd = pro.getProperty(“password”);
//System.out.println(user+”\t”+pwd);
}
…
}
连接池技术
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.编写配置文件c3p0-config.xml 放置在src文件下
<!-- 连接池参数 --><br /> <!--初始化申请的连接数量--><br /> <property name="initialPoolSize">5</property><br /> <!--最大的连接数量--><br /> <property name="maxPoolSize">10</property><br /> <!--超时时间--><br /> <property name="checkoutTimeout">3000</property><br /> </default-config>
<named-config name="otherc3p0"> <br /> <!-- 连接参数 --><br /> <property name="driverClass">com.mysql.jdbc.Driver</property><br /> <property name="jdbcUrl">jdbc:mysql://localhost:3306/petsys?characterEncoding=utf-8</property><br /> <property name="user">root</property><br /> <property name="password"></property>
<!-- 连接池参数 --><br /> <property name="initialPoolSize">2</property><br /> <property name="maxPoolSize">3</property><br /> <property name="checkoutTimeout">3000</property><br /> </named-config><br /> </c3p0-config><br /> <br />3.获取连接池对象<br /> //获取连接池的对象<br /> DataSource dataSource = new ComboPooledDataSource();<br />4.编写获取连接的方法<br /> public Connection getConn() {<br /> //从连接池中获取连接<br /> Connection conn = null;<br /> try {<br /> conn = dataSource.getConnection();<br /> } catch (SQLException e) {<br /> // TODO Auto-generated catch block<br /> e.printStackTrace();<br /> }<br /> return conn;<br /> }<br />5.继续编写关闭方法及增删改方法<br /> 注意:<br /> 此处关闭并不是删除连接 而是将连接归还到连接池 <br /> 连接池保存配置文件中最小连接数量 如果使用数量超出最小空闲连接 才会自动创建连接<br /> 创建的新连接和原始连接总数不超过最大连接数 <br /> 如果超出 则等待时间超过最大等待时间后报错
```
五子棋项目
javaFX java开发桌面应用的工具
Sun公司于2008年12月05日发布的,可以开发互联网应用程序(RIA)。
JDK11以后需要单独下载javaFX模块
舞台
场景
画板
节点:控件 形状
设计的类extends Application
需要重写的方法start(){}
//画板上鼠标点击事件
pane.setOnMouseClicked(new EventHandler
//参数MouseEvent event保存有当前鼠标点击的信息
@Override
public void handle(MouseEvent event) {
//点击后操作的内容
}
}
只带一个确定按钮的提示框
Alert info = new Alert(AlertType.INFORMATION,”此处不能落子”,ButtonType.OK);
info.show();
网络编程
网络编程基础
CS应用程序:拥有两端 客户端 服务器端 要使两端信息进行一个互通
网络通讯协议:
网络编程及网络信息互通依赖的基础
TCP/IP协议:安全性高
UDP协议:无连接通讯协议,数据发送端和接收端不建立逻辑连接 安全性低 有可能导致信息的缺失 但是消耗资源少 传输效率快 一般可用于 音频 视频的传输 例如视频会议。
TCP/IP协议:三次握手(重要理论)称为 面向连接的特性 能够保证数据的安全性
第一次握手:客户端向服务端发送请求 等待服务器端响应
第二次握手:服务端回写信息给客户端 通知客户端服务器端收到信息3
第三次握手:客户端再次向服务端发送请求连接
以上三次握手成功后 真正建立连接 才能够进行数据的传递
客户端主动发送请求 服务器端只负责监听端口 发现客户端提交的请求 所以要求服务器端要一直开启(要优先于客户端开启)并时刻监听端口
网络编程的三要素
协议
IP地址:确定互联网中每台电脑的唯一标识
IPv4:例如 127.0.0.1 是一个32位的二进制数 通常被分为四个字节 xxx.xxx.xx.xxx 其中每个字节都是0-255之间的十进制整数 组合排列后可以表示42亿个标识
IPv6:IP地址新的定义 采用128位地址长度 每16个字节一组 分成8组十六进制数 例如:AB89:AB89:AB89:AB89:AB89:AB89:TY08:AG89 足够每一台电脑使用
ipconfig查看IP地址信息
127.0.0.1 localhost 代表本机
端口号:
使用IP地址来确定哪台PC 使用端口号来确定哪个服务(进程)
使用两个字节表示的整数 取值范围是0~65535 普通应用程序包括我们自己做的服务 可以给大于1024的端口号
通信程序
明确客户端和服务器端
服务器端:需要事先开启 等待客户的连接 java开发中方法所在类java.net.ServerSocket
创建一个ServerSocket对象 就是开起了一个服务 监听端口 等待连接
ServerSocket server = new ServerSocket(8888);
public Socket accept(){} 侦听并接受连接 返回一个新的Socket对象 用于和客户端进行数据互通
客户端:需要发出请求前开启即可 java开发中方法所在类java.net.Socket
创建一个Socket对象 就是创建一个客户端 可以发送请求去对应端口号 等待响应
参数为指定主机和指定端口号
Socket socket = new Socket(“服务器IP地址”,8888);
Socket类 又称为套接字
成员方法:
public InputStream getInputStream(){}
当前socket开启一个读入字节流
public OutputStream getOutputStream(){}
开启一个写出字节流
public void close(){}
关闭当前Socket对象
目标:
服务器端 启动 创建ServerSocket 等待连接
客户端 创建Socket对象 请求连接
服务器端 收到请求 调用accept()获取一个Socket对象
客户端 Socket对象 获取OutputStream 向服务端写出数据
服务器端 Socket对象 获取InputStream 读取客户端发送的数据
服务器端 Socket对象 获取OutputStream 向客户端发送数据
客户端 Socket对象 获取InputStream 读取这个数据
网络编程代码案例:
线程
```
线程
进程:正在运行的程序
线程:进程中一个执行的小单元
关系:进程中至少有一个线程 而一个线程一定属于某个进程
一个进程运行过程中有不止一个线程 那么就是一个多线程应用程序
多线程应用程序 每个线程去争夺CPU的时间片段
CPU线程调度:
抢占式调度:优先级可以在一定程度上影响线程抢到CPU时间片段的几率 但不绝对 当优先级相同时 则随机选择一个线程
时间调度:所有线程轮流使用CPU的使用权 平均分配给每个线程时间
多核处理器:相当于在单核的线程切换中 使之切换效率翻倍 并没有提高程序的运行速度 只是提高了CPU的使用率
主线程:Main() 从启动项目 启动JVM 由虚拟机去找操作系统开辟线程 CPU有了一个要执行的路径(main方法) 路径名(线程名) 就是“main”
守护线程:GC线程 垃圾回收线程
案例:
一个窗口 两人取钱 每人每次取10块 自己取自己的
//线程类
public class Users extends Thread {
int money=0;
//每个线程都要执行的内容 放置在run()中
@Override
public void run() {
// TODO Auto-generated method stub
//super.run();
do {
money+=100;
//this.getName()获取线程的名字
System.out.println(this.getName()+”总共取了”+money+”元”);
}while(money<1000);
}
}
//展示一下main方法的主线程 “main”
System.out.println(Thread.currentThread().getName());
//创建两个对象
Users u1 = new Users();
u1.setName(“张三”);//给线程起名字
u1.start(); //以线程的方式启动主动调用run()
Users u2 = new Users();
u2.setName(“李四”);
u2.start();
线程中start()和run()的区别
run()内编写线程抢占到资源后要执行的内容
start()开启线程 主动调用run()
如果使用线程.run() 则只会认为是调用了一个普通方法 不会有线程的效果
优先级:
线程优先级默认
//获取线程优先级
System.out.println(u1.getPriority());
//设置线程优先级
u1.setPriority(Thread.MIN_PRIORITY);
Thread.NORM_PRIORITY—-》5 默认优先级
Thread.MIN_PRIORITY——》1 最低优先级
Thread.MAX_PRIORITY——》10最高优先级
线程停止:
以下三种不再推荐使用
t1.resume();//关闭1
t1.stop();//关闭2
t1.suspend();//关闭3
建议使用run方法中控制while循环 不要直接用break
while(判断) {
}
案例二:
多个线程共享一个数据 那么建议使用Runnable接口
因为只能创建一个对象 以多个线程来表示操作者
//线程类实现Runnable接口
public class Users implements Runnable {
int money=1000;//多线程共同操作的数据
//为了关闭线程声明的boolean变量
public static boolean b = true;
@Override
public void run() {
// TODO Auto-generated method stub
while(b) {
if(money>0) {
money-=100;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+”余额:”+money+”元”);
}else {
b=false;
}
}
}
}
使用Runnable接口的测试类
//创建线程类对象 只创建一次 实现了Runnable接口
Users u = new Users();
//Thread thread = new Thread(Runnable runnable);
//Thread thread = new Thread(Runnable runnable,String name);
//创建了两个线程 同时放入同一个线程类对象 就可以是t1 t2两个线程共同操作一个数据
Thread t1 = new Thread(u, “张三”);
Thread t2 = new Thread(u, “李四”);
t1.start();
t2.start();
//使用匿名内部类的方式 创建线程 因为Runnable是一个标准的函数式接口 所以还可以升级为lambda表达式
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
}
},”王五”);
t3.start();
简写为:
Thread t3 = new Thread(()->{},”王五”);
t3.start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
}
},”马六”).start();
简写为:
new Thread(()->{},”马六”).start();
Thread 和 Runnable的区别
接口更适合于资源的共享
可以避免java单继承原则的约束
代码可以被多个线程共享 实现了代码和线程的独立 解耦性
线程池只能放入实现Runnable或Callable类的线程 不能直接放入继承Thread的类
如果需要操作同一个对象 不能使用Thread
线程方法带返回值的线程 Callable接口
interface Callable
public T call();
}
案例:计算器 传入两个数字 实现相加的结果
public class MyCall implements Callable
int x;
int y;
public MyCall(int x, int y) {
super();
this.x = x;
this.y = y;
}
@Override
public Integer call() throws Exception {
// TODO Auto-generated method stub
if(Thread.currentThread().getName().equals(“pool-1-thread-1”)) {
Thread.currentThread().setName(“张三”);
}else {
Thread.currentThread().setName(“李四”);
}
System.out.print(Thread.currentThread().getName());
return this.x+this.y;
}
}
测试类使用线程池去执行Callable的任务
线程池:
能够容纳多个线程的容器 可定义其中的线程数量 不需要反复创建线程对象
1.先定义线程功能类 实现Runnable或者Callable接口
2.创建线程池 规定线程数量 获取线程池对象
//创建线程池 规定内部有几个线程 获取线程池的对象
ExecutorService pool = Executors.newFixedThreadPool(2);
3.取出线程 提交线程任务
Future<?> subRun1 = pool.submit(Runnable接口的实现类对象);
Future<抽象方法返回值类型> subCall = pool.submit(Callable接口的实现类对象);
subCall.get();—->获取Callable实现类中方法的返回值
4.pool.shutdown(); //交回线程
//动态创建线程池
ExecutorService pool = Executors.newCachedThreadPool();
pool.execute(Runnable对象);
pool.shutdown();
线程安全问题
多个线程同时操作同一个数据 并且其中有数据的修改操作 大概率会出现数据异常问题 就是线程安全问题
数据在内存中修改的速度慢于了线程切换的速度
解决方案:
给线程上锁 目的:同一时刻 只能有一个线程对象执行线程任务 在这个对象完成线程任务前 其他线程对象不能进入 称之为线程同步机制(线程通信的一种形式)
三种解决方案:
方案一:添加同步代码块
while(b) {
synchronized (this) {
if(money>0) {
money-=100;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+”余额:”+money+”元”);
}else {
b=false;
}
}
}
synchronized(锁对象){
}
锁对象:就只是一个标记 可以为任意类型的对象 Objece obj = new Object();
要保证多个线程使用锁的对象是同一个
锁住同步代码块包裹的代码内容 只让一个线程在同步代码块中执行
方案二:添加同步方法
将要上锁的代码放入同步方法中
public synchronized void run1() {
if(money>0) {
money-=100;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+”余额:”+money+”元”);
}else {
b=false;
}
}
//普通方法中套同步代码块
public void run2() {
synchronized (o) {
if(money>0) {
money-=100;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+”余额:”+money+”元”);
}else {
b=false;
}
}
}
方案三:使用lock锁(接口)
//创建锁对象
Lock l = new ReentrantLock();
@Override
public void run() {
// TODO Auto-generated method stub
while(b) {
l.lock();//上锁
if(money>0) {
money-=100;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+”余额:”+money+”元”);
}else {
b=false;
}
l.unlock();//解锁
}
}
练习:
有100张票 两个用户购买 显示某某某买到了第几张票
线程通信:
线程对象.wait(); 无限等待
当某个线程被等待后 及时代码块内部未执行完 后面的线程都可以开启执行 直到当前这个线程被唤醒
线程对象.notify();唤醒线程
notifyAll();唤醒全部线程
线程中断:
线程对象.isInterrupted();判断线程是否是处于中断状态 中断:true
线程对象.interrput();强制线程对象中断
线程礼让:
线程对象.yield();
在本身优先级比较高的线程中某一个条件时 需要执行其他线程 那么可以让本线程礼让
线程强制执行:
线程对象.join();
强制执行某个线程 执行完毕后 之前的线程才继续执行
线程休眠:
线程对象.sleep(毫秒数); 线程休眠不等于中断 该线程依然在执行
案例:
十名用户 两个窗口 每名用户执行相同的三个操作 1.xxx进入了窗口 2.xxx办理完了业务 3.xxx离开了窗口
十名用户 就是 十个线程 两个窗口 指的就是 (同时)可以执行两个线程
解决方案
1.可以直接使用线程池 控制线程池中线程的数量 从而控制几个任务同时执行
2.可以添加信号量对象 设置同时可以执行几个线程
//设置信号量对象 实际就是控制同时几个线程进入执行
Semaphore sh = new Semaphore(3);
sh.acquire();//类似于上锁过程 只不过同时可以允许信号量中设置的线程数量执行
sh.release();//当前执行的线程数量不足设置值时 可放行其他线程 以达到设置的线程数目
案例:
三个人同时到达现场后 完成拍照 再各回各家
解决方案:
使用同步屏障功能 设置线程数目的要求
总数未达到要求是 所有线程被阻止在设置屏障处
达到数目要求后 所有线程才继续向下执行
//拍集体照的动作 应该放在同步屏障对象中 设置要求获取几个线程才继续向下执行
//在满足条件之前 其他线程都被阻止在设置屏障等待的位置
CyclicBarrier cb = new CyclicBarrier(3);//可以只设置要求的线程数目
CyclicBarrier cb = new CyclicBarrier(3, new Runnable() {
//如果所有线程有需要共同完成的任务 可以加在第二个参数处
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println(“人到齐了 拍摄集体照片”);
}
});
在每个线程等待的位置 设置屏障判断及等待方法
//线程中设置屏障 到达此处后会去判断屏障条件是否达成 没达成则当前线程等待
cb.await();
//达到条件后 可以走屏障处共有方法
//或者各线程走自己后续的代码
第二阶段
讲解大纲(一)
一、视图
- 概念
将有关联的表创建成一张视图表,生成的视图表是一张虚拟表,只能完成数据的查询和修改,不能完成数据的删除和新增。 优缺点:
优点:可以将有关联的表,创建成视图,供下一次查询的时候,可以当成单表查询,提高程序员编写代码的速度。
缺点:
1、只能做查询和修改操作,不能做删除和新增操作
2、视图并不是越多越好,因为视图一旦创建,要求原数据库表中的字段不能做任何的修改,如果做了修改,那么视图崩溃。
二、数据库编程—-变量及控制语句
变量
系统变量
定义:在数据库管理系统里面,在设计的时候,就已经规定好了的变量,一般用来控制整个数据库管理系统,比如mysql数据库管理系统。
系统变量的语法:@@变量名
查询系统变量:show variables;(查询所有的系统变量)show variables like "%名称%"(查询部分系统变量)
会话变量(用户变量/全局变量)
程序员根据编程需要,在数据库的本次会话中创建一个变量,供下面代码使用。
会话变量语法:@变量名
添加一个会话变量:set @变量名 = 值;
查询一个会话变量:select @变量名;
每一次修改会话变量的值,必须加set关键字局部变量
作用在部分代码片段中的变量,一般是在函数、存储过程、触发器中的变量,在begin和end代码之间存在,必须还要用declare定义声明
定义局部变量:declare 变量名 数据类型 default 值;
控制语句
分支语句
— 第一种情况
IF 判断语句 THEN
当判断语句为真的时候,执行的代码
END IF;
— 第二种情况
IF 判断语句1 THEN
当判断语句1位真的时候,执行的代码;
ELSEIF 判断语句2 THEN
当判断语句2位真的时候,执行代码;
ELSE
如果上面情况都不满足,执行的代码;
END IF;
— 分支语句的嵌套
IF 判断语句 THEN
IF 判断语句 THEN
执行语句
END IF;
END IF;
- 循环语句
WHILE 判断语句 DO
循环的代码;
END WHILE;
— 数据库中跳出循环的方法:
leave:终止循环的代码,和java中的break作用一致。
iterate:结束本次循环,继续下一次循环,和java中的continue作用一致。
当有多层循环的时候,要跳出指定层次的循环,需要使用跳出循环的标记。
— 举例:
WHILE 判断语句1 DO
log:WHILE 判断语句2 DO
WHILE 判断语句3 DO
循环的代码;
IF 判断语句 THEN
leave log;
END IF;
END WHILE;
END WHILE;
END WHILE;
三、数据库编程—-函数
概念:将实现特定功能的代码片段进行封装,需要使用的时候,调用函数即可,提高代码的复用性,是封装的最小体现单元。
系统函数
— 聚合函数
count();
max();
min();
avg();
sum();
— 其他聚合函数
now();获取当前的瞬时时间戳
year();获取字段中的年份
UUID();生成一个36位不重复的字符串,后续数据库要使用UUID生成主键。
LAST_INSERT_ID();最后一次新增数据的主键值(必须是主键自增)。
新增函数
— 语法:
CREATE function 函数名(参数列表) RETURNS 返回值数据类型
BEGIN
实现特定功能的函数代码片段
END
— 举例:计算两个数和
— 修改数据库结束标识符
delimiter &
create function add1() returns int
begin
— 定义一个局部变量,记录两个数的和
declare res int default 0;
set res = 10 + 20;
return res;
end &
查询函数
— 查询语法:查询所有的函数
show function status;
— 使用模糊查询到程序员自定义的函数
show function status like “%add1%”;
调用函数
— 语法
select 函数名();
删除函数
— 语法
drop function 函数名;
函数课堂练习讲解
习题1
— 定义一个具有参数的函数
create function add2(a int,b int) returns int
begin
declare res int default 0;
set res = a + b;
return res;
end
习题2
— 计算1-100的累加和
create function add3() returns int
begin
— 定义一个局部变量,用来记录两个数的和
declare res int default 0;
— 定义一个变量,用来记录循环判断
declare i int default 1;
while i <= 100 do
set res = res + i;
set i = i + 1;
end while;
— 返回值
return res;
end
习题3
— 计算1-100之间的所有能被3和7同时整除的数之和
create function add4() returns int
begin
— 定义一个局部变量,来记录两个数的和
declare res int default 0;
— 定义一个局部变量,来记录循环判断
declare i int default 1;
— 循环的代码
while i <= 100 do
if i % 3 = 0 and i % 7 = 0 then
set res = res + i;
end if;
set i = i + 1;
end while;
— 返回值
return res;
end
习题4
— 定义一个函数,计算x的y次方。x和y的值由参数传入。
create function fun(x int,y int) returns double
begin
— 定义一个变量,用于记录最终的结果
declare res double default 1.0;
declare i int default 0;
declare j int default 0;
— 判断指数的大小(大于0?小于0?等于0)
if y > 0 then
while i < y do
set res = res x;
set i = i + 1;
end while;
elseif y < 0 then
set y = y (-1);
while j < y do
set res = res * x;
set j = j +1;
end while;
set res = 1 / res;
else
set res = 1.0;
end if;
return res;
end
四、数据库编程—-存储过程(了解)
概念:和函数的概念一致,将实现特定功能的代码片段封装起来,放在一个存储过程中,需要使用的时候调用即可。关键字为:procedure
和函数的区别
1、函数存在返回值,存储过程没有返回值。
2、函数的参数,只有参数名和数据类型;存储过程参数,有参数名,数据类型,参数类型。
创建过程
— 语法:
create procedure 过程名称(参数列表)
begin
实现存储过程的代码片段;
end
— 举例:
create procedure pro1()
begin
— 定义变量,记录两个数的和
declare res int default 0;
— 定义一个变量,用于判断
declare i int default 1;
while i <= 100 do
set res = res + i;
set i = i + 1;
end while;
select res;
end
查询过程
— 查询所有的存储过程
show procedure status;
— 模糊查询存储过程
show procedure status like “%pro%”;
调用过程
call 存储过程名称();
删除过程
drop procedure 存储过程名称;
参数列表
参数列表:参数类型 参数名 数据类型
重点:参数类型
in:将存储过程外面的变量可以传入到存储过程里面使用。外面的变量:会话变量。
out:可以把存储过程里面操作过的会话变量的值传到存储过程外面。
inout:既可以将存储过程外面的值传入到里面使用,也可以将里面修改过后的值,作用到存储过程外面。
参数类型举例代码
— 定义三个会话变量
set @num1 = 1;
set @num2 = 2;
set @num3 = 3;
— 查询三个会话变量
select @num1,@num2,@num3;
— 定义一个存储过程,三个形式参数,参数类型分别为in,out,inout
create procedure pro3(in a int,out b int,inout c int)
begin
— 查询三个形式参数的值
select a,b,c; — 1,2,3
— 查询三个会话变量
select @num1,@num2,@num3; — 1,2,3
— 修改形式参数的值(局部变量)
set a = 10;
set b = 20;
set c = 30;
— 查询一遍a,b,c的值
select a,b,c; — 10,20,30
— 查询三个会话变量
select @num1,@num2,@num3; — 1,2,3
end
— 调用存储过程
call pro3(@num1,@num2,@num3);
— 再次查询会话变量
select @num1,@num2,@num3;
结论
- 如果参数类型为out类型,那么在实际参数传递给形式参数的时候,形式参数将抛弃实际参数的值,先将其置为null(形式参数)
- 当整个存储过程执行结束之后,返回去检测每个形式参数的参数类型,如果是out或者inout,那么将形式参数修改过后的值,作用到实际参数上。
五、数据库编程—-触发器(掌握)
概念:封装一段代码片段(一般都是对数据的增、删、改的操作),当达到了触发器的条件的时候,自动调用并执行。不需要程序员主动用代码进行调用。
创建触发器
create trigger 触发器名称 触发时机 触发事件 on 表名 for each row
begin
封装的代码片段;
end
触发时机:after和before
触发事件:insert、update、delete
— 创建触发器
CREATE TRIGGER tri1 AFTER INSERT ON t_order FOR EACH ROW
BEGIN
— 修改商品表中的数据
UPDATE t_product SET p_number = p_number - new.o_number where p_id = new.p_id;
END
— 说明:
new.o_number:刚刚新增数据中字段的数据
new.p_id:刚刚新增数据中字段的数据。
查询触发器
— 查询触发器
show triggers;
删除触发器
drop trigger 触发器名称
六、数据库编程—-索引(重点)
概念:索引的作用就是增加数据库的查询效率,索引本质上就是将一个字段有顺序的生成为一个数据结构(树结构),在mysql中存在主键默认加索引。
优缺点
- 优点:索引会增加查询效率
- 缺点:会增加维护成本,主要维护成本是在新增数据、删除数据和修改数据的时候维护,所以会降低这三个操作的效率;索引的本质是数据结构,会占用一定的内存空间。
注意:索引只是一种技术,索引是存在引擎里面的,数据库管理系统可能是不一样引擎,因此数据库会因为引擎的不一样而对索引类型的支持不一样。
索引的类型
- BTREE索引:B树索引(B+树),基本上大部分引擎都支持B数索引
- Hash索引:只有在menory引擎才会支持该索引
- Full-text索引:全文索引,一般在搜索功能里面会用到。
- 唯一索引:主键用的就是唯一索引
- 空间索引:
B树索引:多路平衡搜索树,不一定是二叉树,可以是M叉树。
举例说明一下B树的生成过程,以5叉树为例
25、19、23、21、18、27、16、13、29、28、26、31、24、17、33、15
讲解大纲(二)
一、**WoniuMall项目原型分析
二、数据库设计(**E-R图和数据字典)
三、**XML概述及语法**
XML**文件的概述**
概念:XML是一种可以描述数据之间层级关系的配置文件。可扩展的标记语言。可以根据程序员的需求
自己进行添加标签对数据进行标记。
XML**文件的应用场景:作为框架的配置文件
XML**文件的语法
语法包含:声明、标签、属性、注释、CDATA区、指令(引入其他的文件,比如css)、特殊字符(转义
字符)
xml文件的声明:
标签:用尖括号括起来的就是标签(元素)
属性:在标签里面编写的变量称为属性
注释:对xml文件的说明
CDATA区(数据区):不管xml当中是否用标签标记,如果写在CDATA区,那么浏览器都会当成普通数
据处理
特殊字符:转义字符
<?xml version=”1.0” encoding=”UTF-8” ?>
name:称为属性
<![CDATA[
任何数据
]]>四、**Java解析XML文档
java解析XML文档的方式有两种:SAX和DOM解析
SAX**解析:需要使用数据的时候,一层一层的去遍历找正确的值,读取效率很低,但是节约内存
DOM**解析:会一次性将xml文件中的所有数据,读取到内存中,生成一个Document文档树对象,读取
效率高,但是会消耗内存
使用**java代码实现xml文件解析
查找数据
修改数据
小于符号:<
大于符号:>
//步骤1:根据工厂类获取工厂对象
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();//2
//步骤2:根据工厂类对象获取解析器
DocumentBuilder builder = factory.newDocumentBuilder();//2
//步骤3:根据解析器,获取文档树对象
Document document = builder.parse(new File(“src/xml文件名”));//2
//获取要用的节点
NodeList list = document.getElementsByTagName(“name”);//2
//获取容器中的指定下标的元素
Node node = list.item(index);//1
//获取该节点处的内容
String name = node.getTextContent();//1
//步骤1:根据工厂获取工厂对象
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
//步骤2:根据工厂获取解析器
DocumentBuilder builder = factory.newDocumentBuilder();
//步骤3:根据解析器,获取文档树
Document document = builder.parse(new File(“src/xml文件名”));
//获取节点
NodeList list = document.getElementsByTagName(“name”);
//获取节点下的元素
Node node = list.item(index);
//修改节点的内容
node.setTextContent(“新的值”);
//获取transfromer工厂对象
TransformerFactory tf = TransformerFactory.newInstance();
//根据工厂对象获取transformer对象
Transformer former = tf.newTransformer();
//更新到硬盘中
former.transform(new DOMSource(document), new StreamResult(new
File(“src/book.xml”)));新增数据:不能直接新增内容,必须要新增标签,再在标签里面写上内容
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new File(“src/book.xml”));
//创建新的标签
Node node = document.createElement(“price2”);
//对新的标签设置内容值
node.setTextContent(“89.9”);
//获取要插入标签的父标签
Node parent = document.getElementsByTagName(“b1”).item(0);
//将新创建的标签,用父标签添加为子标签,只能放在这个父标签的最后一个位置
parent.appendChild(node);
//将内存中的文档树对象更新到硬盘中
TransformerFactory tf = TransformerFactory.newInstance();
Transformer former = tf.newTransformer();
former.transform(new DOMSource(document), new StreamResult(new
File(“src/book.xml”)));
//在指定标签的前面添加新标签的方式
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new File(“src/book.xml”));
//创建一个新的标签
Node node = document.createElement(“prices”);
node.setTextContent(“69.9”);
//获取要在哪个标签前面插入的节点对象
Node n = document.getElementsByTagName(“author”).item(0);
Node parent = document.getElementsByTagName(“b1”).item(0);
parent.insertBefore(node,n);
//将内存中的文档树对象更新到硬盘中
TransformerFactory tf = TransformerFactory.newInstance();
Transformer former = tf.newTransformer();
former.transform(new DOMSource(document), new StreamResult(new
File(“src/book.xml”)));
删除标签
public class XMLDemo2 {
//声明一个文档树对象
private Document document;
@Before
public void before() {
//获取文档树对象
try {
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
document = builder.parse(new File(“src/book.xml”));
} catch (Exception e) {
e.printStackTrace();
}修改属性
五、**Junit单元测试**
作用:让普通的方法,可以不用主函数的时候,直接自己执行。
当前测试的方法,在方法的上面加@Test注解,这个方法不能加static修饰,也不能有形式参数
想要在测试方法之前执行的方法,在方法的上面加@Before注解,这个方法不能加static修饰,也不能
有形式参数
想要在测试方法执行之后,执行的方法,在方法的上面加@After注解,这个方法不能加static修饰,也
不能有形式参数
六、开源日志包**Logback的使用(暂时不讲)**
七、**ORM框架Mybatis的基本介绍(包括配置文件)**
原生**JDBC完成
}
@After
public void after() {
try {
//将删除后的文档树更新到硬盘中
TransformerFactory tf = TransformerFactory.newInstance();
Transformer former = tf.newTransformer();
former.transform(new DOMSource(document), new StreamResult(new
File(“src/book.xml”)));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void delete() {
try {
//获取要删除的标签节点
Node node = document.getElementsByTagName(“author”).item(1);
//通过它的父标签删除自己
node.getParentNode().removeChild(node);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Test
public void updateAttr() {
//获取标签
Element e = (Element) document.getElementsByTagName(“name”).item(0);
/
如果该标签中没有该属性和属性值,则表示添加
如果该标签中已经有了这个属性和属性值,则表示修改
/
e.setAttribute(“id”, “nn”);
}public class Demo{
private static String driver = “com.mysql.jdbc.Driver”;
private static String url = “jdbc:mysql://localhost:3306/db_shop”;
private static String username = “root”;
private static String password = “”;
static{
//加载驱动
Class.forName(driver);
}
public static void main(String[] args){
try{
Connection conn =
ManagerDriver.getConnection(url,username,password);
String sql = “select * from t_user where id = ?”;
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1,user.getInt());
ResultSet rs = ps.executeQuery();
while(rs.next()){
}
rs.close();
ps.close();
conn.close();
}catch(Exception e){
}
}
}
问题总结**
原生的jdbc会频繁的创建链接和释放资源,会增加数据库的链接压力
解决方法:使用连接池,用连接池来管理数据库的链接和释放资源。
原生的jdbc的sql语句是写在java代码中的,这种方式属于硬编码,不利于sql代码的维护。
解决方法:mybatis框架将sql语句提取到xml配置文件中,每一次修改sql语句,只需要修改配置文
件中的语句即可,和代码无关
原生的jdbc预编译对象中的?也属于硬编码
解决方法:mybatis框架将视线关系对象映射,mybatis存在输入对象映射和输出对象映射。
mybatis**概念**
用来解决java对象和数据库之间数据交互的框架,apache公司提供的轻量级的ORM框架。目的:将程序
员的所有精力全部放在编写sql语句的思路上,至于执行sql语句,填充占位符和查询的数据与对象进行
关联,全部由mybatis框架完成。
图示**mybatis示例代码梳理:以根据id值查询单条数据为例
编写SqlMapConfig.xml文件
useUnicode=true&characterEncoding=utf-8”/>
<!— 加载
编写失血模型对象:Product,最好字段名和数据库里面的字段名一直
/
Title: Product
Description: 商品类失血模型
@author Alon
@date 2020年9月27日 下午6:51:57
@version 1.0
/
public class Product {
private int p_id;
private String name;
private int p_number;
private double price;
private String add_time;
public Product(int p_id, String name, int p_number, double price, String
add_time) {
super();
this.p_id = p_id;
this.name = name;
this.p_number = p_number;
this.price = price;
this.add_time = add_time;
}
public Product() {
super();
}
public int getP_id() {
return p_id;
}
public void setP_id(int p_id) {
this.p_id = p_id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getP_number() {
return p_number;
}
public void setP_number(int p_number) {
this.p_number = p_number;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getAdd_time() {
return add_time;}
public void setAdd_time(String add_time) {
this.add_time = add_time;
}
@Override
public String toString() {
return “Product [p_id=” + p_id + “, name=” + name + “, p_number=” +
p_number + “, price=” + price
+ “, add_time=” + add_time + “]”;
}
}
编写单个映射关系的sql.xml:product.xml
<?xml version=”1.0” encoding=”UTF-8”?>
PUBLIC “-//mybatis.org//DTD Mapper 3.0//EN”
“http://mybatis.org/dtd/mybatis-3-mapper.dtd“>
resultType=”com.woniuxy.model.Product”>
SELECT
编写java代码进行测试执行sql语句并得到结果
/
Title: MybatisDemo1
Description: 查询数据的demo
@author Alon
@date 2020年9月27日 下午7:05:32
@version 1.0
/
public class MybatisDemo1 {
public static void main(String[] args) throws IOException {
new MybatisDemo1().queryById();
}
public void queryById() throws IOException {
//1、读取到SqlMapConfig.xml文件的流
String path = “SqlMapConfig.xml”;
InputStream config = Resources.getResourceAsStream(path);//创建会话工厂,同时将SqlMapConfig.xml里面的数据放到工厂中
SqlSessionFactory factory = new
SqlSessionFactoryBuilder().build(config);
//开启会话
SqlSession sqlSession = factory.openSession();
/
第一个参数:要执行哪个sql语句的参数,一般命名空间.id值
第二个参数:传入填充#{value}的传入参数值。
/
Object obj = sqlSession.selectOne(“test.findById”, 1);
System.out.println(obj);
}
}
进行模糊查询:查询出多条结果集
resultType=”com.woniuxy.model.Product”>
SELECT FROM t_product WHERE name like “%${value}%”
public void queryByName() throws Exception{
String path = “SqlMapConfig.xml”;
//获取流对象
InputStream config = Resources.getResourceAsStream(path);
//获取会话工厂
SqlSessionFactory factory = new
SqlSessionFactoryBuilder().build(config);
//开启会话
SqlSession sqlSession = factory.openSession();
/
因为执行结果有多条语句,那么必须使用selectList
/
List