第一章 java概念和开发注意事项
java特点
- Java语言是面向对象的(oop)
- Java语言是健壮的。java的强类型机制、异常处理、垃圾的自动收集等是Java程序健壮性的重要保证
- Java语言是跨平台性的 [即:一个编译好的.class 文件可以在多个系统下运行,这种特性称为跨平台] 根本原因是JVM是java虚拟机 解释和执行是靠JVM,包含在JDK
- Java语言是解释型的[了解]->解释性语言:javascript,PHP java 编译性语言:c/c++
- 区别是:解释性语言,编译后的代码,不能直接被机器执行,需要解释器来执行,编译性语言编译后的代码,可以直接被机器执行,c/c++
java的安装过程
什么是JDK,JRE
● JDK 基本介绍
1.JDK 的全称(Java Development Kit Java开发工具包)
JDK=JRE +iava的开发工具[iava,javac,iavadoc,javap等]
2.JDK是提供给Java开发人员使用的,其中包含了java的开发工具,也包括了JRE所以安装了JDK,就不用在单独安装JRE了。
● JRE 基本介绍
1.JRE(ava Runtime EnvironmentJava运行环境)
JRE = JVM +Java的核心类库[类]
2.包括Java虚拟机(JVM Java Virtual Machine)和Java程序所需的核心类库等如果想要运行一个开发好的Java程序,计算机中只需要安装JRE即可:
编译过程
1.有了java源文件,通过编译器将其编译成JVM可以识别的字节码文件
2.在该源文件目录下,通过javac编译工具对Hello.java文件进行编译。
如果程序没有错误,没有任何提示,但在当前目录下会出现一个Hello.class文件该文件称为字节码文件,也是可以执行的java的程序。
开发注意事项和细节
1.Java源文件以 .java 为扩展名。源文件的基本组成部分是类(class),如本类中的Hello类。
2.Java应用程序的执行入口是main()方法。它有固定的书写格式:public static void main(Stringll args) {…}
3.Java语言严格区分大小写。
4.Java方法由一条条语句构成,每个语句以“;”结束。
5.大括号都是成对出现的,缺一不可。[习惯,先写 {} 再写代码]
6.-个源文件中最多只能有一个public类。其它类的个数不限。[演示],编译后,每一个类,都对于一个.class
7.如果源文件包含一个public类,则文件名必须按该类名命名!
8.一个源文件中最多只能有一个public类。其它类的个数不限,也可以将main方法写在非public类中,然后指定运行非public类,这样入口方法就是非public的main方法,就是把其他非public的类当主类运行
如何掌握新知识点
1.学习新技术和知识点不要考虑细节,能使用就行
2.传统技术和新技术有什么优点
4.crud在工作上基本能搞定
5.开始研究技术细节,最后研究这个,没有之境
java转义字符
在控制台,输入 tab 键,可以实现命令补全
第一个\是转移 \\ 输出两个一个是转义字符
\t :一个制表位,实现对齐的功能 (固定宽度)
\n :换行符
\ :一个\
” :一个”
‘ :一个’ \r :一个回车 System.out.println(“韩顺平教育\r)
转移字符使用:System.out.println(“北京\t 天津\t 上海”);
文档注释
javadoc -d 文件名 生成的文件在这里
代码规范
常用的DOS(命令)
1.查看当前目录是有什么内容 dir
dir dir d:\abc2\test200
2.切换到其他盘下:盘符号 cd : change directory
案例演示:切换到 c 盘 直接 c: 更目录下切换
3.切换到当前盘的其他目录下 (使用相对路径和绝对路径演示), ..\表示上一级目录
案例演示: cd d:\abc2\test200 cd ….\abc2\test200
4.切换到上一级: cd ..
5.切换到根目录:cd
6.查看指定的目录下所有的子级目录 tree
7.清屏 cls
8.退出 DOS exit
路径讲解
\ 根目录就是最顶的目录 也可以理解为D盘 绝对路径
Java API 文档
第二章 变量
变量的注意事项
1.变量表示内存中的一个存储区域[不同的变量,类型不同,占用的空间大小不同比如:int4 个字节,double 就是 8个字节,先有基本印象,后面说字节]
2.该区域有自己的名称[变量名]和类型[数据类型]
3.变量必须先声明,后使用,即有顺序
4.该区域的数据/值可以在同一类型范围内不断变化
5.变量在同一个作用域内不能重名
6.变量=变量名+值+数据类型,这一点请大家注意。变量三要素
程序中加号的使用
1.当左右两边都是数值型时,则做加法运算
2.当左右两边有一方为字符串,则做拼接运算
3.运算顺序,是从左到右
数据类型
String在java不是基本数据类型 它是一个类
引用类型都是地址
整数类型
整数类型的使用细节
浮点类型
浮点类型使用细节
5.12e2 = 5.12×100 e = 10的2次方 e-2 10的负二次
浮点数陷阱:8.1 / 3 = 2.69999997 计算机存储的时候是以精度来存储的,计算机问题,计算结果时以精度来返回这个值
8.1有可能是后面8.100001计算机不知道后面有什么
重要点: 当我们对运算结果是小数的进行相等判断,要小心
解决方法:应该是以两个数的差值的绝对值,两个数相减得到的,在某个精度范围类判断
字符类型
char类型 直接赋值数字的话,输出是对应的ascll
字符类型的使用细节
注意:转义字符和普通字符一样,都是占用一个字节的空间。合起来 表示一个转义字符,
强制转换输出是对应的数字,
例如:a输出97, char类型也可以运行,例如:char a = ‘a’+10 println(a) 这里是char类型输出,
println(‘a’+10) 这里和整数运算 输出是数字
println默认打印的是整形,但是可以用printf格式化打印
字符类型的本质探讨 (面试或测试)
布尔类型(boolean)
使用细节:不可以0或非 0的整数替代false和true,这点和C语言不同
基本数据类型转换
自动类型转换
自动类型转换和细节
计算后是int类型 只要运算了就是int
boolean类型不参与转换,要是赋值给其它类型会报错
字符串转int之类的返回即可
强制类型转换
介绍
自动类型转换的逆过程,将容量大的数据类型转换为容量小的数据类型。使用时要加上强制转换符(),但可能造成精度降低或溢出,格外要注意。
注意与细节
第三点,意思是精度int比char大
强制类型使用方式: (类型)操作数
基本数据类型和String类型的转换
注意:类似String c1 = “zzbc”,转不了int类似的类型,可以看函数的介绍
**println打印函数也是 一旦 “” + 这样结合就是基本类型转String类型了 看+号两边数值做加法运算,两边 有一方是字符串就是拼接运算
注意事项
1.在将String 类型转成 基本数据类型时,要确保String类型能够转成有效的数据,比如:我们可以把”123″,转成一个整数,但是不能把 “hello” 转成一个整数
2.如果格式不正确,就会抛出异常,程序就会终止,这个问题在异常处理章节中,会处理
第三章 运算符
算术运算符
算术运算符是对数值类型的变量进行运算的,在Java程序中使用的非常多
注意:
1). double d=10/4; java中先10/4=2, 然后double =>2.0 double d=10.0/4; 里面就是double运算了,精度提升double转换
2). 取模 在%的本质 公式:a%b = a – a / b * b
3). 前++和后++: 作为独立的语句使用 前++和后++都完全等价于i=i+1,作为表达式使用前++:++i先自增后赋值, 后++:i++先赋值后自增
代码思路分析:1.需求,2思路分析,3.代码
关系运算符
(1)关系运算符的结果都是boolean型,也就是要么是true,要么是false
(2)关系表达式 经常用在 if结构的条件中或循环结构的条件中
逻辑运算符
用于连接多个条件(多个关系表达式)最终的结果也是一个boolean值
说明逻辑运算规则
1). a&b : & 叫逻辑与:规则:当 a 和 b 同时为 true ,则结果为 true, 否则为 false
2). a&&b : && 叫短路与:规则:当 a 和 b 同时为 true ,则结果为 true,否则为 false
3). a|b : | 叫逻辑或,规则:当 a 和 b ,有一个为 true ,则结果为 true,否则为 false
4). a||b : || 叫短路或,规则:当 a 和 b ,有一个为 true ,则结果为 true,否则为 false
5). !a : 叫取反,或者非运算。当 a 为 true, 则结果为 false, 当 a 为 false 是,结果为 true
6). a^b: 叫逻辑异或,当 a 和 b 不同时,则结果为 true, 否则为 fals
赋值运算符
细节:
1 运算顺序从右往左 int num = a + b + c; (这里说的是 等号的右边)
2 赋值运算符的左边 只能是变量,右边 可以是变量、表达式、常量值
int num = 20; int num2= 78 * 34 – 10; int num3 = a;
3 复合赋值运算符等价于下面的效果
比如:a+=3;等价于 a=a+3; 其他类推
4 复合赋值运算符会进行类型转换。
byte b = 2; b+=3; b++ 等价 b = (byte)(b +2) 直接 b = b + 3 就会报错精度提高了,
三元运算符
运算符的优先级
标识符的命名规则和规范
标识符的概念
标识符规范
1.包名:多单词组成时所有字母都小写:aaa.bbb.ccc //比如 com.hsp.crm
2.类名、接口名:多单词组成时,所有单词的首字母大写:XxxYyyZzz [大驼峰]
比如: TankShotGame
3.变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写:xxxYyyZzz [小
驼峰, 简称 驼峰法]
比如: tankShotGame
4.常量名:所有字母都大写。多单词时每个单词用下划线连接:XXX_YYY_ZZZ
比如 :定义一个所得税率 TAX_RATE
5.后面我们学习到 类,包,接口,等时,我们的命名规范要这样遵守,更加详细的看文档.
关键字保留字
保留字:Java 保留字:现有 Java 版本尚未使用,但以后版本可能会作为关键字使用。自己命名标识符时要避免使用这些保留
字 byValue、cast、future、 generic、 inner、 operator、 outer、 rest、 var 、 goto 、const
进制
进制介绍:
对于整数,有四种表示方式:
二进制:0,1 ,满 2 进 1.以 0b 或 0B 开头。
十进制:0-9 ,满 10 进 1。
八进制:0-7 ,满 8 进 1. 以数字 0 开头表示。
十六进制:0-9 及 A(10)-F(15),满 16 进 1. 以 0x 或 0X 开头表示。此处的 A-F 不
键盘输入语句
介绍:在编程中,需要接收用户输入的数据,就可以使用键盘输入语句来获取。Input.java , 需要一个 扫描器(对象), 就是
Scanner
步骤 :
1.导入该类的所在包, java.util.*
2.创建该类对象(声明变量)
3.调用里面的功能
4.System.in 代表从键盘输入
读取字符串的某一个:怎么把字符串转成字符char-> 含义是指把字符串的第一个字符得到,s5.charAt(0)得到 s5 字符串的第一个字符’1′
第四章 程序控结构
程序流程控制介绍
- 顺序控制 程序从上到下逐行地执行,中间没有任何判断和跳转。
- 分支控制
- 循环控制
顺序控制
程序从上到下逐行地执行,中间没有任何判断和跳转。
分支控制
1 单分支 if
2 双分支 if-else
3 多分支 if-else if -….-else
单分支:
双分支:
多分支:
switch分支
只要是有值返回的都是表达式switch流程图:
switch细节
Double不能当返回值
思路:要是有double类型的必须要用switch语句 当表达式那就 强制转换即可
可以强制转换
switch和if的比较
1 如果判断的具体数值不多,而且符合 byte、 short 、int、 char, enum[枚举], String 这 6 种类型。虽然两个语句都可
以使用,建议使用 swtich 语句。
2 其他情况:对区间判断,对结果为 boolean 类型判断,使用 if,if 的使用范围更广
循坏控制
for循坏
基本语法:
124可以随便写注意运行顺序即可 且可以用逗号运算符(自己之前学的)
运行顺序:for(1;2;4){3}
for循坏的注意事项和细节说明
1 循环条件是返回一个布尔值的表达式
2 for(;循环判断条件;) 中的初始化和变量迭代可以写到其它地方,但是两边的分号不能省略。
3 循环初始值可以有多条初始化语句,但要求类型一样,并且中间用逗号隔开,循环变量迭代也可以有多条变量迭代
语句,中间用逗号隔开。
循坏条件最终必须返回一个 ture或false
for(;;)配合break语句 使用很好
编程思路
打印 1~100 之间所有是 9 的倍数的整数,统计个数 及 总和.[化繁为简,先死后活]
1 化繁为简 : 即将复杂的需求,拆解成简单的需求,逐步完成(先完成简单的需求,后在完成其他的,拆解) 编程 = 思想 –练习-> 代码
2 **先死后活 **: 先考虑固定的值,然后转成可以灵活变化的值 (可以设定个比较小的值,后计算,后面在改变量)
思路分析: (先写固定值 后在改)
打印 1~100 之间所有是 9 的倍数的整数,统计个数 及 总和
化繁为简(一步一步来)
(1) 完成 输出 1-100 的值 拆解
(2) 在输出的过程中,进行过滤,只输出 9 的倍数 i % 9 ==0
(3) 统计个数 定义一个变量 int count = 0; 当 条件满足时 count++;
(4) 总和 , 定义一个变量 int sum = 0; 当条件满足时累积 sum += i;
while循坏
while的注意事项
循环条件是返回一个布尔值的表达式
while循环是先判断再执行语句
do while 循坏
多重循坏控制(重点)
1 将一个循环放在另一个循环体内,就形成了嵌套循环。其中,for ,while ,do…while 均可以作为外层循环和内层循环。
【建议一般使用两层,最多不要超过 3 层, 否则,代码的可读性很差】
2 实质上,嵌套循环就是把内层循环当成外层循环的循环体。当只有内层循环的循环条件为 false 时,才会完全跳出内
层循环,才可结束外层的当次循环,开始下一次的循环
3 设外层循环次数为 m 次,内层为 n 次,则内层循环体实际上需要执行 m*n
跳转语句break
break出现在循环体中的switch语句体内时,起作用只是跳出该switch语句体,并不能终止循环体的执行。若想强行终止循环体的执行,可以在循环体中,但并不在switch语句中设置break语句,满足某种条件则跳出本层循环体。
流程:
细节和注意事项
continue跳转语句
continue语句用于循环语句中,作用是不执行循环体剩余部分,直接进行下次循环。
if continue的作用:会跳过当前循环中的代码,强迫开始下一次循环。可以理解为如果没 判断语句就一直进入这个continue就一直跳过,如果 有if就指定跳过
return
介绍:
return使用在方法,表示跳出所在的方法,在讲解方法的时候,会详细的介绍
这里我们简单的提一下。
注意:如果 return 写在 main方法,退出程序..
第五章 数组、排序和查找
数组介绍
数组可以存放多个同一类型的数据。数组也是一种数据类型,是引用类型
数组的使用
使用方法动态初始化1:
把三个int数组 传给a a肯定也是数组来的
数组的引用就是数组的使用,访问,获取
数组名[下标/索引]
动态初始化2
先声明 -> int i[]; 在创建i = new int[10] 先声明数组,再 new 分配空间
静态初始化
值分配好的
数组的注意事项
1.这里也满足自动类型转换
数组赋值机制
1 基本数据类型赋值,这个值就是具体的数据,而且相互不影响。
int n1 = 2; int n2 = n1;
2 数组在默认情况下是引用传递,赋的值是地址 传递地址
地址在堆这边
排序
排序是将一群数据,依指定的顺序进行排列的过程。排序的分类:
1.内部排序:指将需要处理的所有数据都加载到内部存储器中进行排序。包括(交换式排序法、选择式排序法和插入式排序法);
2.外部排序法:数据量过大,无法全部加载到内存中需要借助外部存储进行排序。包括(合并排序法和直接合并排序法)。
冒泡排序
冒泡排序(Bubble Sorting)的基本思想是:通过对待排序序列从后向前(从下标较大的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就象水底下的气泡一样逐渐向上冒。
解析:
冒泡排序:
两个相邻数 比较,把最大的放后面即可,每一次排序最大的数会在最后面,所以每次排序后,减少一次比较即可 ,
相关代码:
多维数组 – 二维数组
length二维数组的使用:
二位数组中,如果直接调用b.length方法,返回的则是b数组的行数,
如果是b[0].length方法则返回的是0行所代表的长度。
内存布局
注意:数组名指向第一行的首元素
二维数组的使用 方式
动态语法1:
1.类型[][] 数组名=new 类型[大小][大小]
2 比如: int a[][]=new int[2][3]
动态语法2:
先声明:类型 数组名[][];
再定义(开辟空间) -> 数组名 = new 类型[大小][大小]
赋值(有默认值,比如 int 类型的就是 0)
使用方式 3: 动态初始化-列数不确
//创建 二维数组,一个有 3 个一维数组,但是每个一维数组还没有开数据空间
int[][] arr = new int[3][];
for(int i = 0; i < arr.length; i++) {//遍历 arr 每个一维数组
//给每个一维数组开空间 new
//如果没有给一维数组 new ,那么 arr[i]就是 null
arr[i] = new int[i + 1] 这里给一维数组开辟空间,arr[i]是指第i行的 所以new给i行开辟空间
静态初始化:
定义 类型 数组名![] = {{值1,值2..},{值1,值2..},{值1,值2..}}
使用即可【固定方式访问]
二维数组使用事项和注意方式
1 一维数组的声明方式有:
int[] x 或者 int x[]
2 二维数组的声明方式有:
int[][] y 或者 int[] y[] 或者 int y[][]
3 二维数组实际上是由多个一维数组组成的,它的各个一维数组的长度可以相同,也可以不相同。比如: map[][] 是
一个二维数组
int map [][] = {{1,2},{3,4,5}},由 map[0] 是一个含有两个元素的一维数组 ,map[1] 是一个含有三个元素的一维数组构成,我们也称为列数不等的二维数组
第6章 面向对象编程(基础部分)(oop)
类与对象
概念:
类就是自己自定义的一个数据类型,对象就是 类的实例 ->(自己理解:类就是一个自己定义的数据类型,对象就是数据类型的实例(实体,把抽象的类,给具体化出来(new),),Cat1就是对象引用 或者对象名)
举例:
1 new Cat() 创建一只猫(猫对象)
2 Cat cat1 = new Cat(); 把创建的猫赋给 cat1 (因为你创建猫的类型是cat类的 所以要Cat cat)
3 cat1只是个名字,new的才是对象的具体化
匿名写法:顾名思义就是没名字,new 类名().方法 匿名使用,这个对象也是在堆里面-但是只能用一次,使用后就被销毁了
也可以 new com.hspedu.cat(); 也可以直接new包下路径的类
类在里面为什么不能创建自己的实例(已自己认证的确是)
在构造函数或类的定义中直接实例化自身会导致无限递归。这是因为当你尝试创建A类的一个新实例时,构造器内部又会立即尝试创建另一个A的实例,这个新的实例又会尝试创建另一个实例,如此循环往复,直到程序因栈溢出错误而崩溃
简述:当类本身自己实例自己,构造器初始化时会又新创建 一个实例 如此反复,当你new时构造器就会初始化,如果自己调用自己就会一直new
比如 class A{A a = new A} 构造器new时开辟空间后初始化,发现还有一个new就是死讯坏
对象在内存中的布局
首先 对象是一个引用类型,所以有地址,地址存在堆区 ,堆区里的属性String又是引用类型(地址) 所以存放的是地址在,堆区的引用类型(地址)存在方法区的常量池, 堆区的基本类型存放在堆里,
在new时会加载类的信息在方法区,new的时候会在堆开辟空间地址(属性new的时候是默认值 后来才赋值)
属性与成员变量(字段,属性,成员变了)
1 从概念或叫法上看: 成员变量 = 属性 = field(字段) (即 成员变量是用来表示属性的,授课中,统一叫 属性
2 属性是类的一个组成部分,一般是基本数据类型,也可是引用类型(对象,数组)。比如我们前面定义猫类 的 int age 就
是属性
属性的注意细节
1 属性的定义语法同变量,示例:访问修饰符 属性类型 属性名;
这里老师简单的介绍访问修饰符: 控制属性的访问范围
有四种访问修饰符 public, proctected, 默认, private ,后面我会详细介绍
2 属性的定义类型可以为任意类型,包含基本类型或引用类型
3 属性如果不赋值,有默认值,规则和数组一致。具体说: int 0,short 0, byte 0, long 0, float 0.0,double 0.0,char \u0000,boolean false,String nul
Cat1就是对象引用 或者对象名
注意:
Person p1 = new Person()
p1 是对象名(对象引用)
new Person() 创建的对象空间(数据) 才是真正的对象(new的时候才是真正的对象 实例化)p1只是给了个名字 存放new的对象
如何创建对象和访问对象属性
创建对象:
1 先声明再创建
Cat cat ; //声明对象 cat 此时是null(地址)的,new后把地址赋值给它
cat = new Cat(); //创建
2 直接创建
Cat cat = new Cat()
访问属性:
基本语法
对象名.属性名;
类和对象的内存分配机制(重要)
类加载过后,在堆里面生成class文件
在new时,会把类的信息 加载到方法区
java内存的结构
1 栈: 一般存放基本数据类型(局部变量),栈内存是Java的另一种内存,主要是用来执行程序用的,比如:基本类型的变量和对象的引用变量
2 堆: 存放对象(Cat cat , 数组等)
3 方法区:常量池(常量,比如字符串), 类加载信息(类信息只会加载一次)
成员方法(函数)
用法:访问修饰符 返回类型 方法名字(参数){要运行的代码快}
好处:
提高代码的复用性
可以将实现的细节封装起来,然后供其他用户来调用即可,
调用时才执行,开辟空间
方法的调用机制(内存分析)
注意点: 1.方法在栈开辟独立的空间
2当方法执行完毕 或执行到return语句推出,
3.返回的地方 的调用的地方,哪里调用返回哪里
方法成员的定义
访问修饰符 返回数据类型 方法名(形参列表..) {//方法体
语句;
return 返回值;
}
1 形参列表:表示成员方法输入 cal(int n) , getSum(int num1, int num2)
2 返回数据类型:表示成员方法输出, void 表示没有返回值
3 方法主体:表示为了实现某一功能代码块
4 return 语句不是必须的。(如果没return就是运行到最后才结束方法)
方法使用细节
访问修饰符 (作用是控制 方法使用的范围):
如果不写默认访问 (有四种: public, protected, 默认, private), 具体在后面说
返回数据类型:
1 一个方法最多有一个返回值 (思考,如何返回多个结果 返回数组就是返回多个)
2 返回类型可以为任意类型,包含基本类型或引用类型(数组,对象)
3 如果方法要求有返回数据类型,则方法体中最后的执行语句必须为 return 值; 而且要求返回值类型必须和 return 的
值类型一致或兼容 (兼容就是 返回的数据类型可以自动转换 例如 返回 int 给double)
4 如果方法是 void,则方法体中可以没有 return 语句,或者 只写 return ; 不能有有返回值
方法名:
遵循驼峰命名法,最好见名知义,表达出该功能的意思即可, 比如 得到两个数的和 getSum, 开发中按照规范,在实际工作中,我们的方法都是为了完成某个功能,所以方法名要有一定含义,方法名
首字母小写,如 addOrder() 不要 AddOrder()
动词在前,如 addOrder(),不要orderAdd()
参数列表:
定义是的参数叫形参,调用时叫实参,个数,顺序要一致
不能嵌套定义方法 定义
方法调用细节:
1.同一个类中的方法调用:直接调用即可。比如print(参数);案例演示:A类 sayOk 调用 print() ->就是不用new可以直接名字使用
2.跨类中的方法A类调用B类方法:需要通过对象名调用。比如对象名.方法名(参数);案例演示:B类 sayHello 调用 print()
3.特别说明一下:跨类的方法调用和方法的访问修饰符相关,先暂时这么提一下后面我们讲到访问修饰符时,还要再细说
方法的传承机制(很重要)
引用类型传递的是地址(传递也是值,但是值是地址),可以通过形参影响实参!
坑:
这块代码运行结果是 10,因为你B类的p和上面类的p指向同一个地址,这个b类的p只是赋值,这里把p赋值为null不影响上面,相当于两个同时指向一个
克隆对象
方法的递归(重要)
简单的说: 递归就是方法自己调用自己,每次调用时传入不同的变量,递归有助于编程者解决复杂问题,同时可以让代码变得简洁
1.各种算法中也会使用到递归,比如快排,归并排序,二分查找,分治算法等.
2.将用栈解决的问题–>递归代码比较简洁
递归的执行机制
方法递归的内存:
递归就是 先进后出,后进先出和栈一样,看后一个条件即可
递归重要规则
1 执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
2 方法的局部变量是独立的,不会相互影响,比如n变量
3 如果方法中使用的是引用类型变量(比如数组),就会共享该引用类型的数据.
4 递归必须向退出递归的条件逼近,否则就是无限递归,出现StackOverflowError,死龟了:)
5 当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕。
斐波那契:
看整体 例如:求过的n1(2) = 1 n1(3) = 2 直接代入
猴子偷桃 逆推思维
思路:是从第十天开始计算 使用递归 到达10就返回值1
为啥是 (x+1)*2 因为你吃了一半 再吃一个就不是一半了,要补回去一半,所以+1 你也可以互推看看 数目是否对
(后面桃子 +1) *2 正常 因为你是吃了一半 和多一个 所以
迷宫:
1 findWay 方法就是专门来找出迷宫的路径
2 如果找到,就返回 true ,否则返回 false
3 map 就是二维数组,即表示迷宫
4 i,j 就是老鼠的位置,初始化的位置为(1,1)
5 因为我们是递归的找路,所以我先规定 map 数组的各个值的含义
0 表示可以走 1 表示障碍物 2 表示可以走 3 表示走过,但是走不通是死路
6 当 map[6][5] =2 就说明找到通路,就可以结束,否则就继续找.
上下左右走不通就会 回溯 赋值成3
方法的重载(形参的数据类型个数或顺序需要不一样,方法名字一样,返回类型和重载没关系(无要求))
java中允许同一个类中,多个同名方法的存在,但要求 形参列表不一致!
比如:System.out.println(); out是PrintStream类型
重载的好处:减轻了起名的麻烦 减轻了记名的麻烦
本类使用
使用细节:
注意:传参是如果 传的参数是int 无匹配,但是有一个方法重载doulie类型 也会匹配,如果有int形参优先匹配int
可变参数(方法重载的封装)(当数组使用)
java允许将 同一个类中 多个同名 同功能 但参数个数不同的方法,封装成一个方法。
可变参数使用方式:
当数组使用
基本语法:
这里就是 多个数据类型 ->根据传入的参数匹配
可变参数细节
本质就是数组,可以传数组
第四点是 传参的时候 可变参数和普通的参数可以一起放在传承列表里,但是保证可变参数在最后, (可以把可变参数看成一个参数)
一个形参列表只能出现一个可变参数
作用域
基本使用
{}这样是代码块(以后会讲)
注意事项
构造方法/构造器(初始化对象)
基本语法:
[修饰符] 方法名(形参列表){
方法体;
}
1 构造器的修饰符可以默认, 也可以是 public protected private
2 构造器没有返回值
3 方法名 和类名字必须一样
4 参数列表 和 成员方法一样的规则
5 构造器的调用, 由系统完成
构造器是初始化对象 不是创建对象,当你调用构造器时,对象已经创建了已经在堆里面存在了,空间分配好了
构造器的注意事项
构造器重载和方法 一样
这里有个注意点: 就是一旦自己定义了构造器,默认无参的构造器就会被覆盖,new时如果使用无参构造器就会出错,或者你在写一个无参的
对象内存分析(有构造器)
new 的时候构造器才开始执行,由系统执行
在内存里的对象 刚开始是默认初始化 -> 显示初始化 -> 构造器调用时初始化
this关键词(访问当前对象的 属性,方法,构造器)
引出问题
这里作用域问题,就近原则,最近的变量(局部变量)是在方法里面的,所以name在这个方法结束后,销毁
this(可以理解对象的本身,指向它自己)就是当前对象的属性,什么是当前对象 -> new 的时候创建的对象
this注意细节
1 this 关键字可以用来访问本类的属性、方法、构造器
2 this 用于区分当前类的属性和局部变量
3 访问成员方法的语法:this.方法名(参数列表);
4 访问构造器语法:this(参数列表); 注意只能在构造器中使用(即只能在构造器中访问另外一个构造器, 必须放在第一
条语句) 语法必须要这样写 因为this和类重叠的,this和填入相关参数
5 this 不能在类定义的外部使用,只能在类定义的方法中使用。
本章小结
return 返回null,也是返回地址
编程思维,例如:猜拳游戏,可以把出拳0 1 2,定义一个函数,比赛订一个函数,猜拳结果定一个函数,->就是把这些模块定义成函数调用,先分析拆开,一块一块 在把获取的出拳存到二维数组,和结果也存到
第七章 面向对象(中级)
类加载(类在什么时候被加载)
类会在以下几种情况下被加载:
(1)类被实例化:当你创建类的一个实例(通过 new 关键字)时,与该实例相关的类将被加载。
(2)访问类的静态成员:如果你访问一个类的静态字段或静态方法,该类将被加载。
(3)通过反射访问类:使用反射机制(Class.forName() 或 .class 字面量等)来访问一个类时,它会被加载。
(4)启动时加载的类:JVM在启动时会加载一些系统类,例如java.lang.Object。
(5)初始化一个类的子类:如果一个类有子类,当子类被初始化时,父类也会被加载。
(6)调用类的主方法:当你通过 java 命令运行一个类的 main 方法时,该类将被加载。
类销毁:
该类所有的实例都已经被回收,也就是java堆中不存在该类的任何实例
类信息是什么:
Class就是一个普通的类,就是用来描述一个类的信息的(比如类有几个字段,几个方法,名字叫什么等等 )
可以通过 3 种方法来获取Class的对象,也就是某个类的字节码
有个某个类的字节码以后,我们就知道知道这个类的许多信息了
Class一般是在运行时使用,你只要告诉我类名,我就可以知道这个类中有多少方法,有多少字段,怎么调用等等
类加载器过程
网上找到的资料:
每个类在加载的时候都会创建一个字节码对象,,例如:dog.java ->javac dog.java,生成 java.class文件,
首先是将类加载到方法区的class内容区,这里有两个类Person.class和PersonDemo.class,会被加载到class内容区。
当运行程序的时候,需要用到一个类,会先到方法区查找class信息,如果找到了就拿来用。如果没找到,就会像上面这样,将class类文件加载到方法区里面。(只会加载)
遇到new关键字的时候,将需要将创建对象的类加载 初始化初始化给对象
加载步骤:在.class加载到方法区时,先加载父类再加载子类;先加载静态内容,再加载非静态内容,到这里为止,整个类的加载就完成了
.class文件读入内存,让类加载完之后会生成一个Class类型对象,这个对象会包含类的数据结构,包含所有信息(比如类名,方法,变量等)
静态变量是地址
idea
在idea里 run一个文件时, 会先编译成 .class ->在运行) ,class文件(字节码)在out目录下
//说明一下diagram IDEA properties 的含义
查看类图,properties 点击会显示 setxxx getxxx 的属性
快捷键: 快捷键都是对应单词的
1 删除当前行, 默认是 ctrl + Y 自己配置 ctrl + d (delete)
2 复制当前行, 自己配置 ctrl + alt + 向下光标(duplicate)
3 补全代码 alt + /
4 添加注释和取消注释 ctrl + / 【第一次是添加注释,第二次是取消注释】
5 导入该行需要的类 先配置 auto import(General) , 然后使用 alt+enter 即可
6 快速格式化代码 ctrl + alt + L (搜索:Reformat Code)
7 快速运行程序 自己定义 alt + R () (RUn)
8 生成构造器等 alt + insert [提高开发效率]
9 查看一个类的层级关系 ctrl + H [学习继承后,非常有用]
10 将光标放在一个方法上,输入 ctrl + B , 可以定位到方法 [学继承后,非常有用]
11 自动的分配变量名 , 通过 在后面假 .var [老师最喜欢的] (就是对象后面.var)类.var
模板:
file ->settings -> editor-> Live templates ->
查看有哪些模板快捷键/可以自己增加模板 前期尽量不要使用
包
包的本质
实际上就是 创建不同的文件夹/目录来保存类文件,
例图:
com.xiaoming 点是一级目录点完后是二级目录 .
import 引入包
同一个类名,引入包 第一个引入包了,不用.包
补全就不用引入 直接. (包名.类名 对象名)
包的命名规则与规范
java常用的包
一个包下,包含很多的类,java 中常用的包有:
1 java.lang.* //lang 包是基本包,默认引入,不需要再引入.
2 java.util.* //util 包,系统提供的工具包, 工具类,使用 Scanner
3 java.net.* //网络包,网络开发
4 java.awt.* // 是做 java 的界面开发,
如何引入包
用 只引入一个类比较好,用到哪个类导入那个类
注意事项
注意事项和使用细节
1.package 的作用是声明当前类所在的包需要放在class的最上面,一个类中最多只有一句package
2.import指令 位置放在package的下面,在类定义前面,可以有多句且没有顺序要求。
访问修饰符(可以修饰的对象,属性,方法)
java 提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围): 访问就是 点 .
1 公开级别:用 public 修饰,对外公开
2 受保护级别:用 protected 修饰,对子类和同一个包中的类公开
3 默认级别:没有修饰符号,向同一个包的类公开.
4 私有级别:用 private 修饰,只有类本身可以访问,不对外公开.
方法修饰符的细节
只有默认和public才能修饰类
类只有默认和public才能
注意:一个源文件中最多只能有一个public类
面向对象的三大特征
封装
封装介绍:
封装(encapsulation)就是把 抽象出的 数据(属性)和对 数据的 操作(方法) 封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(方法),才能对数据进行操作。(可以理解为 把复杂的程序封装方法后变成 用户简单简单使用的方法,例如:电视机的开关 使用者只需要开和关,但是内部发生的事情很多)
也就是说,当一个类中的变量被private(私有),他就无法在主程序中直接被调用,那么咱们就需要其他的方法,进行赋值。
封装就是:不让别人随意修改属性,因为有些属性有些要求,例如年龄就是一般都是1-120,把年龄封装起来,然后写业务或安全逻辑,这就是封装
封装的理解和好处:
封装步骤 (把属性私有化似乎是这样)
set方法是给他修改设置属性值的,get方法是给返回属性值
其实可以理解为 就是把属性私有化,保证数据的安全合理,可以对数据验证或者 (自己理解:或者这个封装就是为了属性的私有)
好处:1.对象的属性不能随意修改,2.可以在封装里的方法加入业务逻辑
封装与构造器
构造器可以初始化属性 包括私有属性,这样封装就没用处了,但是把封装的set写到构造器中这样仍然可以验证数据,继续完成业务
继承
继承可以解决代码复用,让我们的编程更加靠近人类思维,当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可。
好处:扩展性变高了,代码复用解决了
基本语法:
继承的基本语法class 子类 extends 父类{ }
1 子类就会自动拥有父类定义的属性和方法
2 父类又叫 超类,基类。
3 子类又叫派生类。
原理图:
开发时 也可以说 D是A的子类
继承的细节
1 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访
问,要通过父类提供公共的方法去访问
2 子类必须调用父类的构造器, 完成父类的初始化 ,(子类构造器 默认自带一个super 父类初始化)
3 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过(怎么理解。) (子类构造器 默认自带一个super 父类初始化,如果父类没有提供无参构造器,子类必须要指定父类构造器初始化 用super)
4 如果希望指定去调用父类的某个构造器,则显式的调用一下 : super(参数列表)
5 super 在使用时,必须放在构造器里面的第一行(super 只能在构造器中使用)
6 super() 和 this() 都只能放在构造器里面的第一行(这两个),因此这两个方法不能共存在一个构造器 (构造器中使用)
7 java 所有类都是 Object 类的子类, Object 是所有类的基类.
8 父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类(顶级父类) (构造器执行也是,其实很容易理解,super方法必须在第一行)
9 子类最多只能继承一个父类(指直接继承),即 java 中是单继承机制。(只能有一个父类,a类不能继承c d两个类 以上)
思考:如何让 A 类继承 B 类和 C 类? 【A 继承 B, B 继承 C】
10 不能滥用继承,子类和父类之间必须满足 is-a (就是 子类 是 父类 的什么,例如 猫是动物 )的逻辑关系
每个子类构造器有一个super,注意隐藏super和this
继承的本质分析
对象的内存:
首先加载son,因为还有父类所以查找父类 加载父类
查找属性或方法时 就近原则
查找顺序:
1 首先看子类是否有该属性
2 如果子类有这个属性,并且可以访问,则返回信息
3 如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息..)
3 如果父类没有就按照(3)的规则,继续找上级父类,直到0biect.
super(用于指定父类)
基本介绍:
super代表父类的引用,用于访问父类的属性、方法、构造器
基本语法:
1 访问父类的属性,但不能访问父类的private属性 super.属性名:
2 访问父类的方法,不能访问父类的private方法 super.方法名(参数列表);
3 访问父类的构造器(这点前面用过):super(参数列表);只能放在构造器的第一句,只能出现一句!
好处与便利:
1 调用父类的构造器的好处“明确,父类属性由父类初始化,子类的属性由子类初始化)
2 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名使用super、this、直接访问是一样的效果
3 super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类中都有同名的成员,使用super访问遵循就近原则。A->B->C
this的查找规则是:先找本类没有,再找父类,(cal()和this.cal()一样)
super:直接查找父类 如果上一个父类没 就上上一个
this和super区别:
方法重写
基本介绍:
简单的说:方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖了父类 (多层的父类也可以)的那个方法
就近原则
方法重写的注意事项
多态(难度最大)
多(多种)态(状态)介绍:
方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
1.方法的多态: 重写或重载体现多态,就是我们传入不同的参数调用不同的方法
2.对象的多态(多态的核心):
感觉是父类的引用指向了子类的对象)
(1)一个对象的编译类型和运行类型可以不一致
(2)编译类型在定义对象时,就确定了,不能改变
(3)运行类型是可以变化的.编译类型看定义时 =号 的左边,运行类型看=号的 右边
例如:animal 编译类型是Animal,可以指向(接收),Aaimal子类的对象
可以理解为,就是用父类的引用,把子类的类赋值给父类,然后每次传入参数 可以随意变化,因为运行类型是子类的 右边,
Animal animal = new dog(); 参数是父类Animal 所以传入 子类 可以 用 父类的引用调用
其实多态就是 一个父类的 引用指向子类的地址, 子类开辟空间就把地址传过去
得到运行类型就是 得到这个对象关联的class这个对象
多态的注意事项和细节讨论
多态的向上转型:
不能调用子类中的特有成员,这就是向上转 父类引用指向子类
为什么不能调用? 因为在编译阶段,能调用那些成员 是由 编译类型决定的,运行类型只是javac后的
最终运行效果看子类(运行类型)的具体实现,即调用方法时,按照从子类(运行类型)开始查找方法然后调用,规则我前面我们讲的方法调用规则一致。
多态的向下转型:
此时的运行类型和编译类型 都是子类
多态的属性和比较对象函数
属性没有重写之说, 属性的值看 编译类型,在使用时看编译类型
判断 xx 是 xx 的 类型或着子类 , 看右边运行类型,有两个一个是类型 一个是子类
xx看的是运行类型 应该是xx的运行类型是xx的类型或子类
==用来比较两个对象的地址是否相等。,主要用来上下转换时的类型
Java的动态绑定(非常重要)多态的
java的动态绑定机制: 和多态密切相关的
(1)当调用对象方法的时候,该方法会和该对象的运行类型绑定 ,如果运行类型没这个方法就会往上找
(2)当调用对象的属性时,没有动态绑定机制,哪里声明就在哪里使用
多态的应用
(1)多态数组:
多态数组 数组定义为父类类型,里面保存子类类型,因为父类的引用可以指向子类对象
(2)多态参数:
方法定义的形参类型为父类类型,实参类型允许为子类类型
Object类
==方法
equals方法
只能判断引用类型
equals的 this 是 xxx.equals(xxx) xxx.就是xxx.的
其实 .equals 可以理解为 xxx对象的调用方法, xxx对象继承了Object类,调用Object的equals方法,this是找本类的对象,
this就是当前对象 ,this就是谁调用就是谁是对象
hashCode
Java是跑在虚拟机上的,所以是无法拿到这个对象的地址
toString
finalize (当对象被回收时,系统会自动调用该对象的这个方法)
程序员就可以在 finalize中,写自己的业务逻辑代码(比如释放资源:数据库连接,或者打开文件。)
断点调试
第八章 面向对象(高级)
类变量(static)(用处:统计某些数)(可以修饰的对象,属性,方法)
static 不能修饰构造器 因为构造器是new时触发的,static是类加载的
存放在 堆区 静态变量 类变量是随着类加载而创建的
类加载是java JVM虚拟机在运行时进行的操作
对应类加载了 就会生成一个class实例 class里面就包含了static变量
共识:不管static 变量在哪里 (1)static 变量是同一个类所有对象共享(2)static类变量,在类加载的时候就生成了.
类变量:类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。这个从前面的图也可看出来。
父类拥有类变量 子类继承后 子类的 类名.属性 也可以调用,因为子类加载类时,父类也会加载,所以子类可以用
如何定义类变量:
定义语法:
(1)访问修饰符 static 数据类型 变量名;
(2)(推荐)static 访问修饰符 数据类型 变量名;
访问类变量:
(1)类名.类变量名
(2)对象名,类变量名
静态变量的访问修饰符的访问权限和范围 和 普通属性是一样的。
推荐使用:类名,类变量名:
区别:
数据存储位置不同
成员变量存储在堆内存的对象中,所以也叫对象的特有数据
静态变量存储在方法区(共享数据区)的静态区,所以也叫对象的共享数据
类变量注意事项和访问细节
类方法(用处:不想实例化,对象)
类方法的经典使用:
类方法的注意事项
this的参数
main方法语法
java命令时所传递的参数 的 java 类名 sss lll
特别注意:
1)在main()方法中,我们可以直接调用main方法所在类的静态方法或静态属性。
2)但是,不能直接访问该类中的非静态成员,必须创建该类的-通过这个对象去访问类中的非静态成员,
和静态方法一样其实
idea main传参
代码块
基本介绍:
代码块好处:
1)相当于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作
2)如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性
因为多个构造器只能运行一个,所以可以使用代码块代码快初始化先
注意事项
调用顺序:
隐含 super后 **调用本类的普通代码块和普通的成员 ** 如果在构造器里面写,按顺序执行从上到下,首先super
知道有隐含的就可以
super 和 this 永远在第一句话 而且只能存在一个
设计模式
什么是设计模式
1.静态方法和属性的经典使用
2.设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格,以及解决问题的思考方式 。设计模式就像是经典的棋谱,不同的棋局,我们用不同的棋谱,免去我们自己再思考和摸索 ,不同的棋局,不同的套路
遇到类似的问题,就可以使用这种模式 相应
单例模式
单例(单个的实例)
1.所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个 取得其对象实例的方法
2.单例模式有两种方式:1)饿汉式 2)懒汉式
比如一个类的资源很大
饿汉式:
原理:只使用一个对象,没使用这个对象,但是还是自己创建了
饿汉式可能创建了没有使用,可能会浪费资源,因为饿汉式的对象都是资源重的
懒汉式:
懒汉式相当于 多了一个方法,这个方法调用时如果对象为null创建,如果不为空就就返回已经创建好的对象(上图右边)
final关键字
final使用细节
第七点 :final和static搭配使用,这个类不会加载,(面试时可说)有时候不需要加载用此方法
final 当作形参可以,赋值一次 值不能改变
抽象类(abstract)
当父类的一些方法不能确定时,可以用abstract关键字来修饰该方法,这个方法就是抽象方法,用abstract 来修饰该类就是抽象类。抽象方法没有方法体,和C语言一样声明
(不确定有什么作用,写了也没什么意义,所以可以声明抽象方法,抽象方法没有实现所以没有方法体,一般来说抽象类会被继承,由子类实现 重写)
抽象类的介绍
语法:abstract class na class na是创建一个类所以 abstract是访问修饰符 class 是创建 访问修饰符在前面
抽象类的细节和注意事项
1)抽象类不能被实例化
2)抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法
3)一旦类包含了abstract方法,则这个类必须声明为abstract
4)abstract 只能修饰类和方法,不能修饰属性和其它的。
5)抽象类可以有任意成员【因为抽象类还是类】,比如:非抽象方法、构造器、静态属性等等
6)抽象方法不能有方法体, abstract void ooo();
7)如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类。
8)抽象方法不能使用private、final 积static来修饰,因为这些关键字都是和重写相违背的
private是因为要子类 static是类加载和重写无关 final是因为不能被重写
这里也是动态绑定机制 因为是继承,关于继承都是动态绑定机制,看运行类型
抽象类最佳实践-模板设计模式
父类更好管理
接口
接口其实 就是实现接口的抽象方法
基本介绍:
最重要是可以管理,例如:写一个方法参数是接口,把对应接口传入会自动判断
接口的注意事项
接口也叫继承
接口与继承
因为继承是 单继承的只能有一个父类,实现接口就是 对 单继承的补充
当子类继承了父类,就自动拥有了父类的功能,因为是单继承,如果想给子类扩展功能,可以通过接口interface
接口和继承的关系
接口的多态
内部类
基本介绍:
内部类的分类:
成员就是属性或方法
局部内部类(本质上还是一个类)
局部内部类就是一个 局部变量 如果是外部类 new一下会在堆空间开辟一个地址
外部类使用局部内部类 要在局部作用域创建对象,在调用它的方法即可 必须在作用域
也就是说外部类使用 局部内部类,只能让局部内部类在定义的位置,new然后使用 其他外部类无法访问
记住一句话,局部内部类 它定义的位置
作用域在什么地方
匿名内部类(很重要) (匿名内部类的名字 有$)
只使用一次,没有引用后会自动销毁就可以使用匿名内部类
new 类/接口(){}; 后面打了大括号就是匿名内部类 类名是$
(1)基于接口的
例子:
接口是不能直接 new的,所以 直接用 大括号 把接口的方法写清楚,写完后加个new,底层就会知道你这个是匿名内部类。
代码:
new Calculator() {
@Override
public void getOperating() {
System.out.println(“计算器启动”);
}
};
灵活使用演示:
(2)基于类的(也可以基于抽象类的匿名类,但是抽象方法要实现)
注意:匿名内部类可以理解为,new时加个大括号,底层就知道你的是匿名内部类了,底层的运作是,一个匿名的引用 实现或者继承
然后把 这些地址给传到引用的类
匿名内部类的注意事项
上面两个使用方式
匿名内部类 最佳使用方式: 当作实参直接传递
例如: void dd(In in) 可以 直接 dd(new In{ 里面写实现方法}); 就相当于匿名类的引用赋值给了
这样会方便很多 用完就销毁
成员内部类
使用的话 在外部类定一个返回内部类里面的属性或者方法
右边的代码是 外部其他类访问成员内部类 第一个方式可以理解为 外部类的属性然后 点. ,第二种方式 和第一种一样,就是直接new了后 在.在new一下 第一个的结合
第三个方法 是用一个方法返回 内部类 即可
静态内部类
外部其他类 访问 静态内部类 就是外部类.内部类就可以了 因为是静态 静态可以调用类名
因为都是在类里面
第十章 枚举和注解
枚举(enum开发一个枚举类,默认会继承enum)
枚举介绍:
1)枚举对应英文(enumeration,简写 enum)
2)枚举是一组常量的集合。
3)可以这里理解:枚举属于一种特殊的类,里面只包含一组有限的特定的对象
枚举的两种实现:
1)自定义类实现权举
枚举就是 一组常量 不可被修改的,所以要把构造器给私有,只给一个get方法,给一个暴露对象方便调用
小结:
2)使用enum 关键字实现枚举
enum后 类变私有,外面访问不到,所以构造器可以不用私有
枚举的注意事项
javac dog.java -> dog.class javap dog.class -> dog.java
本质还是个类
enum常用方法 (因为使用了enum就会隐式的继承enum)
利用数组方法
注解(Annotation)
注解的理解
Override
@Target修饰注解的注解 叫元注解
Deprecated
修饰某个元素, 表示该元素已经过时了,既不推荐使用,但是还是可以使用, 当被注释的变量,使用时会有下划线
用处的过渡 例如 jdk8 升级到 jdk11 jdk11里面有一个类过度了 就可以用这个注解
suppersWarnings
通常放在方法和类
关于 SuppressWarnings 作用范围是和你放置的位置相关
比如 @SuppressWarnings 放置在 main 方法,那么抑制警告的范围就是 main
该注解类有数组 String[] values() 设置一个数组比如 {“rawtypes”, “unchecked”} 可以传入多个参数
JDK元注解
Retention
Target
Documented
Inherited
第十一章 异常
异常(exception)
就是不是很致命的问题,就会抛出异常 比如:10 / 0
ctrl + alt + t 把包起来的代码
异常介绍
● 基本概念Java语言中,将程序执行中发生的不正常情况称为“异常”(例如:10 / 0)。((开发过程中的语法错误和逻辑错误不是异常)
● 执行过程中所发生的异常事件可分为两类:
- Error(错误):Java虚拟机无法解决的严重问题。如:JVM系统内部错误资源耗尽等严重情况。比如:StackOverflowError(栈溢出)和OOM(out ofmemory),Error 是严重错误,程序会崩溃。
2)Exception: 其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如空指针访问,试图读取不存在的文件,网络连接中断等等,Exception 分为两大类:运行时异常 (程序运行时,发生的异常)和编译时异常(编程时,编译器检查出的异常)。 编译就是还在写代码的时候 ,运行就是运行代码时 (编译异常 必须要处理)
异常的体系图
蓝色的实线是继承,虚线是实现关系
小结:
常见的运行时异常
- NullPointerException空指针异常,当应用程序试图在需要对象的地方使用 null 时,抛出该异常(就是对象还为null时,使用就会抛出异常)
- ArithmeticException数学运算异常当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例
- ArrayIndexOutOfBoundsException数组下标越界异常,用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则,该索引为非法索引
- ClassCastException类型转换异常,将生成一个 ClassCastException,当试图将对象强制转换为不是实例的子类时,抛出该异常。
5)NumberFormatException数字格式不正确异常,当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适,当格式时,抛出该异常 =>使用异常我们可以确保输入是满足条件数字.
编译异常
异常处理
基本介绍:异常处理就是当异常发生时,对异常处理的方式。
异常处理的方式:
- try-catch-finally程序员在代码中捕获发生的异常,自行处理
流程图:
finally 要优先输出
处理异常说明:
1)Java提供try和catch块来处理异常。try块用于包含可能出错的代码。catch块用
于处理try块中发生的异常。可以根据需要在程序中有多个数量的try…catch块。
2)基本语法
try {
//可疑代码
//将异常生成对应的异常对象,传递给catch块
}catch(异常){
//对异常的处理
}
注意细节:
finally 要优先输出
- throws
将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者就是JVM
介绍:
1)如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。
2)在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。
throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类throws (exception)
关键字后也可以是异常列表,即可以抛出多个异常
注意与事项:
运行异常,不要求程序员处理,因为有默认处理机制(或者你抛出是运行有异常,要是抛出是编译异常就报错),但是编译异常一定要程序员处理,否则还没开始就报错了 运行异常的默认处理机制就是一直仍到JVM暴力处理 (默认抛出运行异常)
自定义异常
自定义异常步骤:
1)定义类:自定义异常类名(程序员自己写)继承Exception或RuntimeException,(然后判断条件抛出 throw)
2)如果继承Exception,属于编译异常
3)如果继承RuntimeException,属于运行异常(一般来说,继承RuntimeException)
throw 和 throws区别
finally 要优先输出
也可以throw抛出java已经定义好了 的异常,用来抛出去 然后t – c – f 判断
**getMessage()
返回此throwable的详细消息字符串。 **
可以理解为返回 异常(“”)括号里面的字符串
第十二章 常用类(面试会问到)
包装类
八大wrapper(包装类)类
为什么会有包装类:
Java是一个面相对象的编程语言,基本类型并不具有对象的性质,为了让基本类型也具有对象的特征,就出现了包装类型(如我们在使用集合类型Collection时就一定要使用包装类型而非基本类型),它相当于将基本类型“包装起来”,使得它具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作。
包装类的介绍
面向对象最重要的是继承体系,只要把继承体系搞懂了就不会乱
装箱和拆箱(装箱: 基本数据类型 -> 包装类 拆箱: 包装类 -> 基本数据类型 )
底层调用,debug即可, 把100这样直接传入封箱也可以 例如:Double d = 100d 底层自动封箱 = Double.valuof(100)
自动装箱 就是 JDK帮我包裹了一下 其实还是前面手动一样 底层可查看
先创建obj1 然后判断了true 从ture后是一个整体 会精度提升
包装类(Integer) –> String
String –> 包装类(Integer)
Integer(创建机制)
题目:
包装类的方法
Integer类和Character类的常用方法:
String(重点)
String结构解析
final char[] aa 其实是 地址不可修改,但是char数组里面的值是可以的 , 可以修改里面的字符 但是地址不能修改,也就是不能指向别人 更换地址(及创建新对象) (估计是创建常量池的字符串)相当于每个新的字符串常量就会在常量池 创建新的一个地址
final是常量 指的是数组常量 ,如果是普通的话是字符常量
String创建
.intern()方法是返回字符串常量的地址
String对象特征
面试题:
题目1:
String = “a” + “b” 这种加 是两个相加完看常量池有这个字符串没,如果没有就添加
String = “a” + bb 这种 加上一个变量,底层会创建一个stringbuffer类,初始化一个数组,把这两个值用append()添加进去
题目2:
只要是和变量相加都是new一个新对象
String的常用方法
String的常用方法:
实例1:
实例2:
format和 printf使用方法一致,前面写格式 format(“%d”,sum)
Stringbuffer(对String类的增强,使用在多线程 )
基本介绍:
可以看成字符数组 使用
区别:
Stringffer 构造器
Stringbuffer转换:
Stringbuffer常用方法
替换(9, 11) 从第九个位置开始,11不包含
代码:
String str = null;// ok
StringBuffer sb = new StringBuffer(); //ok
sb.append(str);//需要看源码 , 底层调用的是 AbstractStringBuilder 的 appendNull
System.out.println(sb.length());//4
System.out.println(sb);//null
Stringbuildre(使用在单线程)
基本介绍: 和Stringbuffer一样,但是这个在单线程使用
Stringbuilfre和Stringbuffer和String 区别
选择
:
Math类(数学相关的,查手册即可)
基本介绍
Arrays类
定制排序就是 把系统的接口重写 然后通过动态绑定机制传入
System类
BigInteger类 和 BigDecimal类(大数处理方案比double和long还大)
应用场景:
1)BigInteger适合保存比较大的整型
2)BigDecimal适合保存精度更高的浮点型(小数)
Date类(查手册用即可)
第一代日期类:
date默认输出格式是国外方式,因此通常需要转换
下面是相关代码:
1.获取当前系统时间
2.这里的 Date 类是在 java.util 包
3.默认输出的日期格式是国外的方式, 因此通常需要对格式进行转换
Date d1 = new Date(); //获取当前系统时间
System.out.println(“当前日期=” + d1);
Date d2 = new Date(9234567); //通过指定毫秒数得到时间
System.out.println(“d2=” + d2); //获取某个时间对应的毫秒数
1.创建 SimpleDateFormat 对象,可以指定相应的格式
2这里的格式使用的字母是规定好,不能乱写
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy 年 MM 月 dd 日 hh:mm:ss E”);
String format = sdf.format(d1); // format:将日期转换成指定格式的字符串
System.out.println(“当前日期=” + format);
1.可以把一个格式化的 String 转成对应的 Date
2.得到 Date 仍然在输出时,还是按照国外的形式,如果希望指定格式输出,需要转换
3.在把 String -> Date , 使用的 sdf 格式需要和你给的 String 的格式一样,否则会抛出转换异常
String s = “1996 年 01 月 01 日 10:20:30 星期一”;
Date parse = sdf.parse(s);
System.out.println(“parse=” + sdf.format(parse));
第二代日期类
calendar类(日历)
主要:
相关代码:
1.Calendar 是一个抽象类, 并且构造器是 private
2.可以通过 getInstance() 来获取实例
3.提供大量的方法和字段提供给程序员
4.Calendar 没有提供对应的格式化的类,因此需要程序员自己组合来输出(灵活)
5.如果我们需要按照 24 小时进制来获取时间, Calendar.HOUR ==改成=> Calendar.HOUR_OF_DAY
Calendar c = Calendar.getInstance(); //创建日历类对象//比较简单,自由
System.out.println(“c=” + c);
//2.获取日历对象的某个日历字段
System.out.println(“年:” + c.get(Calendar.YEAR));
这里为什么要 + 1, 因为 Calendar 返回月时候,是按照 0 开始编号
System.out.println(“月:” + (c.get(Calendar.MONTH) + 1));
System.out.println(“日:” + c.get(Calendar.DAY_OF_MONTH));
System.out.println(“小时:” + c.get(Calendar.HOUR));
System.out.println(“分钟:” + c.get(Calendar.MINUTE));
System.out.println(“秒:” + c.get(Calendar.SECOND));
//Calender 没有专门的格式化方法,所以需要程序员自己来组合显示
第三代日期类(JDK8加入)
因为前两个日期都有缺点:
(1)第三代日期类常见方法
(2)datetimeformatter格式日期类
(3)instant(时间戳)
代码:
1.通过 静态方法 now() 获取表示当前时间戳的对象
Instant now = Instant.now();
System.out.println(now);
2.通过 from 可以把 Instant 转成 Date
Date date = Date.from(now);
3.通过 date 的 toInstant() 可以把 date 转成 Instant 对象
Instant instant = date.toInstant()
代码思路:
把正确是写入先,取反就是不正确的
集合
用起来不是很难,理解底层难,在哪里使用
集合和数组的区别:
数组:
数组
1)长度开始时必须指定,而且一旦指定,不能更改
2)保存的必须为同一类型的元素
3)使用数组进行增加元素的示意代码 -比较麻烦
集合:
1)可以动态保存任意多介对象,使用比较方便!
2)提供了一系列方便的操作对象的方法:add、remove、set、get等
3)使用集合添加,删除新元素的示意代码-简洁了
集合的框架体系(最重要)
什么是单列和双列:
1.集合主要是两组(单列集合,双列集合)
2.Collection 接口有两个重要的子接口 List Set ,他们的实现子类都是单列集合
3.Map 接口的实现子类 是双列集合,存放的 K-V
单列集合(单个的):
ArrayList arrayList = new ArrayList();
arrayList.add(“jack”);
arrayList.add(“tom”);
双列集合(一组的):
HashMap hashMap = new HashMap();
hashMap.put(“NO1″,”北京”);
hashMap.put(“NO2″,”上海”);
Collection接口和常用方法
Collection接口实现类的特点:
public interface Collection <E> extends lterable<E>
1)collection实现子类可以存放多个元素,每个元素可以是Obiect (继承Oviect或者他的子类都可以放在这里)
2)有些Collection的实现类,可以存放重复的元素,有些不可以
3)有些Colection的实现类,有些是有序的(List),有些不是有序(Set)
4)Collection接口没有直接的实现子类,是通过它的子接口Set 和 List 来实现的
Colection接口和常用方法
集合的iterator迭代器
介绍:
所有Colloction接口和集合类都有一个iterator()方法, 调用此方法可以返回一个iterator对象
然后调用 iterator方法即可 获取元素
方式1:
方式2(增强for循坏 可用数组):
底层用的还是迭代器
list接口
基本介绍:
list的常用方法
ArrayList 底层是一个数组
List接口的三种遍历方式[ArrayList, linkedList, vector]
Arraylist的注意事项
1)permits all elements, including null , ArrayList 可以加入null,并且多个
2)ArrayList 是由数组来实现数据存储的
3)ArrayList 基本等同于Vector,除了 ArrayList是线程不安全(执行效率高) 看源码在多线程情况下,不建议使用ArrayList, 多线程考虑vector
ArrayList的底层操作机制源码分析
结论:
无参构造器源码:
vector(底层和ArrayList类似)
基本介绍:
vector和ArrayList比较
linkedList
linedList全面说明:
1)LinkedList底层实现了双向链表和双端队列特点
2)可以添加任意元素(元素可以重复),包括null
3)线程不安全,没有实现同步
LinkedList底层机制:
LinkedList底层结构:
添加:
size是节点
ArrayList和linkedlist 比较和选择(单线程使用)
Set接口和常用方法
基本介绍:
注意:取出的顺序的顺序虽然不是添加的顺序,但是他的固定
set接口的常用方法和 遍历方式
Set接口实现类-HashSet
hashSet全面说明:
HashSet底层机制
一个开发小技巧:
不要刚开始创建好辅助变量,在需要的时候创建
分析源码:
同哈希值 才会形成链表 因为
计算名字和 年龄返回哈希值
分析HashSet的扩容和转红黑树机制:
Set接口实现类-LinkedHashSet(HashSet区别是这个是双向链表+数组)
LinkedHashSet全面介绍:
1)LinkedHashSet 是 HashSet 的子类
2)LinkedHashSet 底层是一个 LinkedHashMap(HashMap子类),底层维护了一个 数组+双向链表
3)LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,同时使用链表维护元素的次序(图),这使得元素看起来是以插入顺序保存的。
4)LinkedHashSet 不允许添重复元素
底层分布:
底层机制源码:
Map接口(在开发中经常被使用)
Map接口实现类的特点
key 还是Hashmap 和之前的 LinkedhashMap一样 都是计算哈希值来存放
相当于三层 node 存到 entry entry存到entrySet
Transient Set<Map.Entry<k,v>> entrSet -> 让这两个K,V指向Node节点 (相当于指针指向)
关于EntrySet:
——>当你调用 entrySet() 时,HashMap 会遍历其内部数组,并收集所有非空位置的条目到一个 Set 中(Set 此时等于 EntrySet)。这个 Set 的每个元素都是一个 Map.Entry 对象,你可以通过迭代器或增强型 for 循环来访问这些条目。
代码:创建EntrySet
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? **(entrySet = new EntrySet()) **: es;
}
Map接口的常用方法
Map六大遍历
第一组 :
第二组:
第二组: 把所有的 values 取出
Collection values = map.values();
//这里可以使用所有的 Collections
第三组:
因为Node就是Entry,因为Node实现了Map.Entry所以,Node强转Entry就可以使用entry方法
因为Ertry容易遍历, yweihashMap$Node没有实现相应的方法,所以转成Entry ,本来迭代器是Node类型的
Node包裹K-V EntrSet包裹Node 步骤:刚开始取出EntrySet 存放给 Set, 在给EntrySet迭代器 itertor,里面存放的是Node
因为Node没方法所以用Entry —(Entry 本来就是Node,就是 取出时是Node,因为Node没方法所以要转 )
Map接口实现类-HashMap小结
Map接口实现类-HashMap
HashMap底层机制和源码
源码:
代码解析
1.执行构造器 new HashMap()
初始化加载因子 loadfactor = 0.75
HashMap$Node[] table = null
2.执行 put 调用 hash 方法,计算 key 的 hash 值 (h = key.hashCode()) ^ (h >>> 16)
public V put(K key, V value) {//K = “java” value = 10
return putVal(hash(key), key, value, false, true);
}
3.执行 putVal
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;//辅助变量
//如果底层的 table 数组为 null, 或者 length =0 , 就扩容到 16
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//取出 hash 值对应的 table 的索引位置的 Node, 如果为 null, 就直接把加入的 k-v
//, 创建成一个 Node ,加入该位置即可
if ((p = tab[i = (n – 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;//辅助变量
// 如果 table 的索引位置的 key 的 hash 相同和新的 key 的 hash 值相同,
// 并 满足(table 现有的结点的 key 和准备添加的 key 是同一个对象 || equals 返回真)
// 就认为不能加入新的 k-v
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)//如果当前的 table 的已有的 Node 是红黑树,就按照红黑树的方式处
理
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
//如果找到的结点,后面是链表,就循环比较
for (int binCount = 0; ; ++binCount) {//死循环
if ((e = p.next) == null) {//如果整个链表,没有和他相同,就加到该链表的最后
p.next = newNode(hash, key, value, null);
//加入后,判断当前链表的个数,是否已经到 8 个,到 8 个,后
//就调用 treeifyBin 方法进行红黑树的转换
if (binCount >= TREEIFY_THRESHOLD – 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash && //如果在循环比较过程中,发现有相同,就 break,就只是替换 value
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value; //替换,key 对应 value
afterNodeAccess(e);
return oldValue;
}
}
++modCount;//每增加一个 Node ,就 size++
if (++size > threshold[12-24-48])//如 size > 临界值,就扩容
resize();
afterNodeInsertion(evict);
return null;
}
5.关于树化(转成红黑树)
//如果 table 为 null ,或者大小还没有到 64,暂时不树化,而是进行扩容. //否则才会真正的树化 -> 剪枝
final void treeifyBin(Node<K,V>[] tab, int hash) {
int n, index; Node<K,V> e;
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
resize();
Map接口实现类-HashTable
HashTable基本介绍
扩容机制底层:
1.底层有数组 Hashtable$Entry[]初始化大小为 11
2.临界值 threshold 8=11 *0.75
3.扩容:按照自己的扩容机制来进行即可。
4.执行方法 addEntry(hash,key,value,index);添加K-V 封装到Entry//5.当 if(count >= threshold)满足时,就进行扩容
5.按照 int newcapacity=(oldcapacity<< 1)+ 1;的大小扩容.
HashMap和hashTable区别
:
Map接口实现类-properties
基本介绍:
1.Properties类继承自Hashtable类并且实现了Map接口,也是使用一种键值对的形式来保存数据。
2.他的使用特点和Hashtable类似 比如:null
3.Properties 还可以用于 从 xxx.properties 文件中,加载数据到Properties类对象并进行读取和修改
4.说明: 工作后 xxx.properties 文件通常作为配置文件,这个知识点在I0流举例,有兴趣可先看文章
总结-开发中如何选择集合实现类(重点 记住)
TreeSet
- 当我们使用无参构造器,创建 TreeSet 时,仍然是无序的
2.老师希望添加的元素,按照字符串大小来排序
3.使用 TreeSet 提供的一个构造器,可以传入一个比较器(匿名内部类)
并指定排序规则
代码:
构造器把传入的比较器对象,赋给了 TreeSet 的底层的 TreeMap 的属性 this.comparator
public TreeMap(Comparator comparator) {
this.comparator = comparator;
}
2.在 调用 treeSet.add(“tom”), 在底层会执行到
if (cpr != null) {//cpr 就是我们的匿名内部类(对象) 因为匿名内部类 赋值给了 cpr
do {
parent = t; //把上一个值赋给这个
//动态绑定到我们的匿名内部类(对象)compare
cmp = cpr.compare(key, t.key); //这里匿名接口的比较
if (cmp < 0)
t = t.left; //这里是连接 只需要做好比较就行
else if (cmp > 0)
t = t.right;
else //如果相等,即返回 0,这个 Key 就没有加入
return t.setValue(value);
} while (t != null);
}
TreeMap
使用默认的构造器,创建 TreeMap,** 是无序的也没有排序**
第一次添加, 把 k-v 封装到 Entry 对象,放入 root
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check 主要检查是否 是NULL值
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
第二次后添加
Comparator cpr = comparator; //
if (cpr != null) {
do { //遍历所有的 key , 给当前 key 找到适当位置
parent = t;
cmp = cpr.compare(key, t.key);//动态绑定到我们的匿名内部类的 compare
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else //如果遍历过程中,发现准备添加 Key 和当前已有的 Key 相等,就不添加 key不添加,Value添加
return t.setValue(value);
} while (t != null);
Comparator接口 比较器
Comparator可以自己写 new时添加
一个比较接口,如果传入参数有此接口,可自定义排序指定排序 规则
Collections工具类(都是静态的 可以直接调用 类名.方法)
List 就是 接口 实现这个接口的都可以使用
boolean replaceAll() 里面参数是: 如果list 里有oldVal的元素 替换成 newVal的元素
泛型
传统的方法:
1)不能对加入到集合 ArrayList中的数据类型进行约束(不安全)
2)遍历的时候,需要进行类型转换,如果集合中的数据量较大,对效率有影响
泛型的好处:
其实泛型 E就是 一个数据类型 接收另一个引用类型 E = Object
泛型的介绍
注意->特别强调: E具体的数据类型在定义 Person 对象的时候指定(也就是传入的时候),即在编译期间,就确定 E 是什么类, 也就是new时
泛型的语法
TKV 只能是引用类型
泛型使用的注意事项
List<Integer> list2 = new ArrayList<Integer>();
编译器会推断这种写法后面是填充integer类型 这种是用在集合才会自动推断
第三点 -> 这里说的是该类型 的该 类型子类或者 其子类 , 不是<>里面的子类
自定义泛型类-基本语法和注意细节
创建对象 new时,指定,才开始替换
自定义泛型接口
实现接口时,会自动填到相应的位置
自定义泛型方法
使用了泛型 就是方法里面使用了 类的泛型
泛型的继承和通配符
测试工具JUnit
多线程基础
线程的相关概念:
1.程序:
是为完成特定任务、用某种语言编写的一组指令的集合。简单的说:就是我们写的代码
2.进程:
1.进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存空间。当我们使用迅雷,又启动了一个进程,操作系统将为迅雷分配新的内存空间。
2.进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程:有它自身的产生、存在和消亡的过程
3.线程:
1.线程由进程创建的,是进程的一个实体
2.一个进程可以拥有多个线程,
4.并行与并发
并行有可能 并发:当并行任务过多时,就会分出并发
线程的基本使用
创建线程的两种方式
1.继承Thread类 重写run方法(写上业务逻辑) 要用start()启动run方法才是线程。不然只是普通的方法调用
线程流程:
每个线程都可以开一个线程
调用过程:
如果直接调用run()方法就是一个普通的方法, 没有真正的启动一个线程,
start0()是本地方法,是JVM调用,底层是c/c++实现 (是start0里面 利用多线程机制 调用的run方法 )
真正实现多线程的效果,是start0(),而不是run
private native void start();
2.实现Runnable接口 重写run方法:
java是单继承机制,所以有时继承了一个类就无法在继承另一个了类,
我们可以使用了实现Runnable后,在new一个Thread类 传入 实现了Runnable的对象,在调用start方法,在 start方法里面会动态绑定传入的对象,start0里面 利用多线程机制 调用的run方法 此时run方法动态绑定了 实现Runnable的对象
是start0来实现多线程的
Dog dog = new Dog();
//dog.start(); 这里不能调用 start,因为Runnable 没有start方法
//创建了 Thread 对象,把 dog 对象(实现 Runnable),放入 Thread (要实现Runnable)
Thread thread = new Thread(dog);// 此时 在start0 调用的run方法是动态绑定 dog的
thread.start();
多个子线程
当所有线程执行完毕进程也结束
继承Thread vs 实现Rlnable的区别
线程终止(通知终止)
1.当线程完成任务后,会自动退出。
2.还可以通过使用变量来控制run方法退出的方式停止线程,即通知方式
设置一个变量 等时间差不多了把变量改写,例如:run里的while(ture)可以设置成 一个变量 在main主线程里,弄一个延迟函数,到达时,该写变量
线程常用方法
注意事项:
常用方法1:
常用方法2:
礼让不一定成功,因为是根据资源紧急的,如果cpu一个能干完就不会礼让
join插入,需要启动strat子线程
join会抛出异常,可以抛出去 throws InterruptedException直接抛出 即可使用
用户线程和守护线程
1.用户线程:也叫工作线程,当线程的任务执行完或通知方式结束 (一般是这种)
2.守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束 ( 需要调用setDaemon()这个方法设置后,用start启动,一直执行)
3.常见的守护线程:垃圾回收机制
线程的生命周期
JDK中用Thread.State枚举表示了线程的几种状态
(一种是6个,一种是7个->但是里面Runnable里面还分 两个Ready(就绪状态)和runing(运行状态)):
public static enum Thread.State
NEW
尚未启动的线程处于此状态。
RUNNABLE
在Java虚拟机中执行的线程处于此状态。 运行状态
BLOCKED
被阻塞等待监视器锁定的线程处于此状态。
WAITING
正在等待另一个线程执行特定动作的线程处于此状态。
TIMED_WAITING
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
TERMINATED
已退出的线程处于此状态。
线程同步(Synchronized)
线程同步机制介绍:
1.在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性。
2.也可以这里理解:线程同步,即当有一个线程在对内存进行操作时, 其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作.
总的来说 只能有一个线程对该数据操作
一种锁的概念:进入得到锁,出来解开锁下一个才能进
流程:
互斥锁
基本介绍:
必须是 同一个对象,意思是:传入的引用地址要一样,不然在方法块后的对象 指定一个锁不要用this、
所以对象 必须是共享的对象
分析:
1.public synchronized void sell() {} 就是一个同步方法
2.这时锁在 this 对象
3.也可以在代码块上写 synchronize ,同步代码块, 互斥锁还是在 this 对象 (但是这里的锁可以是其他对象,自己:相当于其他线程也是要有这把锁才能 进入方法,所以可以是其它)
4.静态方法的锁是 类本身,代码块时 要写类本身 因为静态方法访问不了普通属性
注意事项和细节:
1.同步方法如果没有使用static修饰:默认锁对象为this
2.如果方法使用static修饰,默认锁对象:当前类.class
3.实现的落地步骤:
需要先分析上锁的代码
选择同步代码块或同步方法
要求多个线程的锁对象为同一个即可!(这里同一个对象 指的是共享的对象,就是锁对象(方法块后面的) 每次都要同一个)
线程的死锁(避免)
多个线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程是一定要避免死锁的发生.
比如:两个同时获得了自己的锁,但是又想获得对方的锁就会线程锁
经典代码:
if (flag) {
synchronized (o1) {//对象互斥锁, 下面就是同步代码
System.out.println(Thread.currentThread().getName() + ” 进入 1″);
synchronized (o2) { // 这里获得 li 对象的监视权
System.out.println(Thread.currentThread().getName() + ” 进入 2″);
}
}
} else {
synchronized (o2) {
System.out.println(Thread.currentThread().getName() + ” 进入 3″);
synchronized (o1) { // 这里获得 li 对象的监视权
System.out.println(Thread.currentThread().getName() + ” 进入 4″);
}
当两个一起启动一个 true 和false就会线程死锁
释放锁
不会释放锁:
IO流
文件基础知识
java程序中对于数据的输入和输出我们是按流进行的
常用的文件操作
创建文件:
案例演示:
调用File的方法才开始处理,调用创建方法就开始创建,调用查看名字方法就开始查看,调用delete方法才开始删除
createNewFile() -> true如果命名文件不存在并被成功创建; false如果命名文件已经存在
获取文件的相关信息:
getName() 获取文件名字
getAbsolutePath() 获取绝对路径
getPatent()获取文件父级目录
length() 获取文件大小
exists() 判断文件是否存在 (在java中 目录也被当作文件)
ifFile() 是否是一个文件
ifDirectory() 是否是一个目录
目录的操作和文件的删除
(在java中 目录也被当作文件)
mkdir创建一级目录、mkdirs创建多级目录、 (创建多个多级目录用mkdirs) 如果用mkdir会报错
delete删除空目录或文件 (目录也被当成文件) 会返回true和false
IO流原理及流的分类
java IO流原理
1.I/O是Input/Output的缩写,I/O技术是非常实用的技术,用于处理数据传输。如读/写文件,网络通讯等。
2.Java程序中,对于数据的输入/输出操作以”流(stream)”的方式进行
3.iava.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过方法输入或输出数据
IO流的分类
InputStream
InputStream:字节输入流
InputStream:抽象类是所有类字节输入流的超类
InputStream 常用的子类:
1.FilelnputStream: 文件输入流
2.BufferedlnputStream: 缓冲字节输入
3.ObjectlnputStream:对象字节输入流
继承图:
filter是过滤输入流
FileInputStream(字节输入流 文件–> 程序) 多利用String
常用方法和构造器:
1.int read(byte[] b) ->如果返回-1 , 表示读取完毕
如果读取正常, 返回实际读取的字节数
字节读取 只读取一个字节,如果汉字的话是 三个字节 存一个汉字,读取一个汉字会乱码
->该方法从输入流读取一些字节数,并将它们存储到缓冲区b 。 实际读取的字节数作为整数返回。 该方法阻塞直到输入数据可用,检测到文件结束或抛出异常。 也就是说从该输入流读取一个字节的数据。 如果没有输入可用,此方法将阻止。等待下一次
2.要关闭文件流释放资源
代码思路:
System.out.print(new String(buf, 0, readLen));//显示 利用构造器把buf byte类型 转成 String类型后打印
FileOutputStream(如果文件不存在,会自动创建这个文件) 要多利用String的方法转byte
代码: 1.new FileOutputStream(filePath, true) 创建方式,当写入内容是,是追加到文件后面
2.写入字符串
String str = “hsp,world!”;
str.getBytes() 可以把 字符串-> 字节数组
fileOutputStream.write(str.getBytes()); 转成字节数组传入
文件的拷贝
//定义一个字节数组,提高读取效果
byte[] buf = new byte[1024];
int readLen = 0;
while ((readLen = fileInputStream.read(buf)) != -1) {
//读取到后,就写入到文件 通过 fileOutputStream
//即,是一边读,一边写 (为什么可以一边读一边写,是因为在一次执行程序中,只有new时会覆盖,指针会指向前面,正常不会)
fileOutputStream.write(buf, 0, readLen);//一定要使用这个方法 因为输入一次1024个
文件字符流
FileReader 文件字符输入
输入肯定是循坏的
用指定部分转成比较好
FileWrite 按字符输出
new时就已经开始覆盖了, 只有new时会覆盖,指针会指向前面
只有刷新或关闭才会把内存里的内容写入文件里 (在此之前都是在内存里) 一定要关流 不然白干了,数据都没写入
这两个方法才是真正的写入
节点流和处理流
节点流:只能操作一种类型
处理流:利用多态,可以传入这个流的子类操作
节点流和处理流的区别
修饰器设计模式: 定义一个抽象类的父类,把父类的方法封装起来自己写,new时传入父类的子类即可,后面在调用时,利用对象的多态绑定机制,绑定到对应的实现子类
BufferedReader和BufferedWriter 字符处理流(按照字符操作的 不要使用二进制)
BufferedReader 和 BufferedWriter 属于字符流,是按照字符来读取数据的
用在文本文件,要是用在二进制文件例如:音频 照片,视频 会有精度损失
关闭时,只需要关闭外层流即可, 因为底层会自动的关闭节点流
代码:
1.bufferedReader.readLine() 是按行读取文件
2.当返回 null 时,表示文件读取完毕
BufferedInputstream 和 BufferedoutputStream字节处理流(按字节输入输出) 但是可以操作文本文件
可以处理文本文件,又可以处理二进制文件
BufferedinputStream:
BufferedOutputStream
ObjectinputStream和ObjectoutputStream(对象处理流)
作用:
例子:
ObjectOutputstream (序列化)
序列化后,保存的格式不是文本格式,要按照它的形式保存
Objectinputstream (反序列化)
要把类拿到才行,才能使用
这里是特别重要的细节:
//1.如果我们希望调用Dog的方法,需要向下转型
2.需要我们将Dog类的定义,拷贝到可以引用的位置 或者 把它写成一个公共public的包 大家都可以访问的包
Dog dog2 =(Dog)dog;
注意事项和注意细节
标准输入输出流
底层 是类型接收的
out 就是 printstram 所以在 打印处理流 可以赋值给打印处理流
in 就是 inputstream
转换流 inputStreamReader 和 outputStreamWriter (名字 字节的父类和字符的父类)
inputStreamReader:
代码:
Outputstreamwriter:
打印流-PrintStream 和 printWriter (打印流只有输出流没有输入流)
printStream 字节打印流:
默认打印到 显示器 ,可以用setOut打印到指定文件
printwriter (字符打印流)
close 才是真正的写内容进去
propertier类
基本介绍:
代码:
- 创建 Properties 对象
Properties properties = new Properties();- 加载指定配置文件
properties.load(new FileReader(“src\mysql.properties”));- 把 k-v 显示控制台
properties.list(System.out);- 根据 key 获取对应的值
String user = properties.getProperty(“user”);
5.将 k-v 存储文件中即可
properties.store(new FileOutputStream(“src\mysql2.properties”), null); (null这里是注释)
反射 Reflection(全部当成对象)
2024-08-13 14:46:08 星期二
反射:Java的反射是指程序在运行期可以拿到一个对象的所有信息。
需求:这样的需求在学习框架时特别多,即通过外部文件配置,在不修改源码情况下,来控制程序, 也符合设计模式的 ocp原则(开闭原则: 不修改源码,扩容功能) 不改变源码的情况下
反射里,全部是对象
类加载过后,在堆里面生成class文件(类对象 放在堆)
在new时,会把类的信息 加载到方法区
每个类都有一个class对象
反射机制的用处(这里可以看动态加载和静态加载区别):
Java反射机制可以完成 (在运行时拿到class类的所有,运行指的是:只有运行到这段代码时才会类加载才会判断有没有这个类)
1.在运行时判断任意一个对象所属的类
2.在运行时构造任意一个类的对象
3.在运行时得到任意一个类所具有的成员变量和方法
4.在运行时调用任意一个对象的成员变量和方法
5.生成动态代理
反射相关的主要类:
1.java.lang.Class:代表一个类, Class对象表示某个类加载后在堆中的对象 (比如 cat 也有对应的class类对象)
2.java.lang.reflect.Method: 代表类的方法, Method对象表示某个类的方法
3.java.lang.reflect.Field: 代表类的成员变量, Field对象表示某个类的成员变量 (私有属性拿不到,因为只能在本类使用私有)
4.java.lang.reflect.Constructor: 代表类的构造方法,Constructor对象表示构造器
反射的优点和缺点
1.优点: 可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支撑。
2.缺点: 使用反射基本是解释执行,对执行速度有影响 (关闭访问检查)
反射优化:
class类
通过class对象 可以完整的得到类的结构
class类常用方法
代码演示:
1.获取到 Car 类 对应的 Class 对象 表示不确定的 Java 类型 默认 Object Class cls = Class.forName(classAllPath);
2.输出 cls
System.out.println(cls); //显示 cls 对象, 是哪个类的 Class 对象 com.hspedu.Car
System.out.println(cls.getClass());//输出 cls 运行类型 java.lang.Class
3.得到包名
System.out.println(cls.getPackage().getName());//包名
4.得到全类名
System.out.println(cls.getName());
5.通过 cls 创建对象实例
Car car = (Car) cls.newInstance();
System.out.println(car);//car.toString()
6.通过反射获取属性 brand 因为反射 里面都是对象
Field brand =** cls.getField(“brand”);**
System.out.println(brand.get(car));//宝马 这里是属性调用方法 里面对象
7.通过反射给属性赋值
brand.set(car, “奔驰”); 属性 在确定那个对象 在修改
System.out.println(brand.get(car));//奔驰
8 我希望大家可以得到所有的属性(字段)
System.out.println(“===所有的字段属性”);
Field[] fields = cls.getFields();
for (Field f : fields) {
System.out.println(f.getName());// 名称
获取Class类对象(Class对象 其实就是运行类型,)
得到运行类型就是 得到这个对象关联的class这个对象
类加载器每对象都有有一个公用的
代码:
- Class.forName 在编译时
String classAllPath = “com.hspedu.Car”; //通过读取配置文件获取
Class< ? > cls1 = Class.forName(classAllPath);
System.out.println(cls1);
2.类名.class , 应用场景: 用于参数传递
Class cls2 = Car.class;
System.out.println(cls2);
3.对象.getClass(), 应用场景,有对象实例 new了的时候
Car car = new Car();
Class cls3 = car.getClass();
System.out.println(cls3);
4.通过类加载器【4 种】来获取到类的 Class 对象
(1)先得到类加载器 car
ClassLoader classLoader = car.getClass().getClassLoader();
//(2)通过类加载器得到 Class 对象
Class cls4 = classLoader.loadClass(classAllPath);
System.out.println(cls4);
//cls1 , cls2 , cls3 , cls4 其实是同一个对象
5.基本数据(int, char,boolean,float,double,byte,long,short) 按如下方式得到 Class 类对象
Class< Integer > integerClass = int.class; 底层会自动封箱后拆箱
Class< Character > characterClass = char.class;
Class< Boolean > booleanClass = boolean.class;
System.out.println(integerClass);//int
6.基本数据类型对应的包装类,可以通过 .TYPE 得到 Class 类对象
Class< Integer > type1 = Integer.TYPE;
Class< Character > type2 = Character.TYPE; //其它包装类 BOOLEAN, DOUBLE, LONG,BYTE 等待
那些类型有Class对象
类加载
这也就是反射的机制 反射是动态加载,只有运行时才会加载此类,而且还能跳过编译,
但是静态加载就不一样了,需要编译的时候就已经开始加载
类的加载过程
类加载:
加载阶段
JVM 在该阶段的主要目的是将字节码从不同的数据源(可能是 class 文件、也可能是jar 包,甚至网络)转化为二进制字节流加载到内存中,并生成一个代表该类的java.lang.Class 对象
连接阶段-验证
1.目的是为了确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
2.包括:文件格式验证(是否以魔数 oxcafebabe开头)、元数据验证、字节码验证和符号引用验证 (class字节码里是cafe的)
3.可以考虑使用 -Xverify:none 参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间。 (面试时可以说,小项目不用关,大项目建议)
连接阶段-准备
1.JVM 会在该阶段对静态变量,分配内存并默认初始化(对应数据类型的默认初始值,如 0、0L、null、false 等)。这些变量所使用的内存都将在方法区中进行分配 (只有静态变量会,普通变量不用理会,静态常量一开始就是初始化定义的值)
连接阶段-解析
1.虚拟机将常量池内的符号引用替换为直接引用的过程, (本来是按符号现在按地址)
Initialization(初始化)
通过反射获取类的结构信息
通过反射获取类的结构信息
第一组:java.lang.Class类 类对象
第二组:java.lang.Reflect.Field类 属性类
第三组:java.lang.Reflect.Method
第四组:java.lang.reflect.constructor类
反射爆破创建
带declared的都可以访问,其他的只是访问public
通过反射创建对象:
class里面每个都是对象,无视私有构造器,
1.有参的话,先创建构造器,后再用构造器调用创建实例化
代码:
1.先获取到 User 类的 Class 对象 (无参构造器) 直接 new
Class< ? > userClass = Class.forName(“com.hspedu.reflection.User”);
通过 public 的无参构造器创建实例
Object o = userClass.newInstance();
System.out.println(o);
2.通过 public 的有参构造器创建实例
constructor 对象就是
public User(String name) {//public 的有参构造器
this.name = name;
}
2.1先得到对应构造器 ->获取构造器先
Constructor< ? > constructor = userClass.getConstructor(String.class); (加s是获取全部)
2.2创建实例,并传入实参 ->在创建class对象实例
Object hsp = constructor.newInstance(“hsp”); (用构造器创建对象)
System.out.println(“hsp=” + hsp);
3.通过非 public 的有参构造器创建实例
3.1.得到 private 的构造器对象 这个方法可以获取所有
Constructor< ? > constructor1 = userClass.getDeclaredConstructor(int.class, String.class);
4.2 创建实例
暴破【暴力破解】 , 使用反射可以访问 private 构造器/方法/属性, 反射面前,都是纸老虎
constructor1.setAccessible(true);
Object user2 = constructor1.newInstance(100, “张三丰”);
通过反射访问类中的成员
访问属性
因为静态方法是在前面加载完成的,所以可以用null调用
代码:
1.得到 Student 类对应的 Class 对象
Class< ? > stuClass = Class.forName(“com.hspedu.reflection.Student”);
2.创建对象
Object o = stuClass.newInstance();//o 的运行类型就是 Student
System.out.println(o.getClass());//Student
3.使用反射得到 age 属性对象
Field age = stuClass.getField(“age”);
age.set(o, 88);//通过反射来操作属性
System.out.println(o);//
System.out.println(age.get(o));//返回 age 属性的值
4.使用反射操作 name 属性
Field name = stuClass.getDeclaredField(“name”);
//对 name 进行暴破, 可以操作 private 属性
name.setAccessible(true);
//name.set(o, “hh”);
name.set(null, “hh”);//因为 name 是 static 属性,因此 o 也可以写出 null
System.out.println(o);
System.out.println(name.get(o)); //获取属性值
System.out.println(name.get(null));//获取属性值, 要求 name 是 static
访问方法
代码:
1.得到 Boss 类对应的 Class 对象
Class< ? > bossCls = Class.forName(“com.hspedu.reflection.Boss”);
2.创建对象
Object o = bossCls.newInstance();
3.调用 public 的 hi 方法
Method hi = bossCls.getMethod(“hi”, String.class);//OK
3.1得到 hi 方法对象
Method hi = bossCls.getDeclaredMethod(“hi”, String.class);//OK
3.2调用
hi.invoke(o, “hhhh~”);
4.调用 private static 方法
4.1得到 say 方法对象
Method say = bossCls.getDeclaredMethod(“say”, int.class, String.class, char.class);
4.2因为 say 方法是 private, 所以需要暴破,原理和前面讲的构造器和属性一样
say.setAccessible(true);
System.out.println(say.invoke(o, 100, “张三”, ‘男’));
4.3因为 say 方法是 static 的,还可以这样调用 ,可以传入 null
System.out.println(say.invoke(null, 200, “李四”, ‘女’));
5.在反射中,如果方法有返回值,统一返回 Object , 但是他运行类型和方法定义的返回类型一致
Object reVal = say.invoke(null, 300, “王五”, ‘男’);
System.out.println(“reVal 的运行类型=” + reVal.getClass());//String, 也就是返回的类型 = 运行类型
JDBC和连接池
JDBC概念
1.JDBC为访问不同的数据库提供了统一的接口,为使用者屏蔽了细节问题
2Java程序员使用JDBC,可以连接任何提供了JDBC驱动程序的数据库系统,从完成对数据库的各种操作。
3.JDBC的基本原理图[重要!]
JDBC带来的好处
JDBC API
JDBC编写步骤
1.注册驱动-加载Driver 类
2.获取连接-得到Connection (拿到连接完成后面)
3.执行增删改查-发送SQL 给mysql执行
4.释放资源-关闭相关连接
//前置工作: 在项目下创建一个文件夹比如 libs (这里把MySQL的jar包添加为库)
// 将 mysql.jar 拷贝到该目录下,点击 add to project ..加入到项目中
//1. 注册驱动 //这里需要更改 需要看MySQL版本和jdbc版本
//com.mysql.jdbc.Driver。自 MySQL Connector/J 8.0 起,旧的驱动类 com.mysql.jdbc.Driver 已经被新的驱动类 com.mysql.cj.jdbc.Driver 所替代。
Driver driver = new Driver(); //创建 driver 对象
//2. 得到连接
//(1) jdbc:mysql:// 规定好表示协议,通过 jdbc 的方式连接 mysql
//(2) localhost 主机,可以是 ip 地址
//(3) 3306 表示 mysql 监听的端口
//(4) hsp_db02 连接到 mysql dbms 的哪个数据库
//(5) mysql 的连接本质就是前面学过的 socket 连接
String url = “jdbc:mysql://localhost:3306/hsp_db02”;
//将 用户名和密码放入到 Properties 对象
Properties properties = new Properties();
//说明 user 和 password 是规定好,后面的值根据实际情况写
properties.setProperty(“user”, “root”);// 用户
properties.setProperty(“password”, “hsp”); //密码
Connection connect = driver.connect(url, properties);
//3. 执行 sql
//String sql = “insert into actor values(null, ‘刘德华’, ‘男’, ‘1970-11-11’, ‘110’)”;
//String sql = “update actor set name=’周星驰’ where id = 1”;
String sql = “delete from actor where id = 1”;
//statement 用于执行静态 SQL 语句并返回其生成的结果的对象
Statement statement = connect.createStatement();
int rows = statement.executeUpdate(sql); // 如果是 dml 语句,返回的就是影响行数
System.out.println(rows > 0 ? “成功” : “失败”);
//4. 关闭连接资源
statement.close();
connect.close();
获取数据库的五种连接方式
上面是第一种
第二种方式(反射创建Driver):
因为第一种方法是静态加载 灵活性差,依赖性强,所以用第二种
利用反射机制:
Class<?> aClass = Class.forName(“com.mysql.jdbc.Driver”);
Driver driver=(Driver)aClass.newInstance(); //(已知类是driver)
String url =”jdbc:mysql://localhost:3306/hsp_db02″,//将 用户名和密码放入到Properties对象Properties properties = new Properties();//说明 user 和 password 是规定好,后面的值根据实际情况写properties.setProperty(“user”,”root”);//用户properties.setProperty(“password”,”hsp”);//密码
Connection connect = driver.connect(url,properties);
System.out.println(“方式2=” + connect);
第三种方法(利用DriverManager类 管理,不需要创建 properties对象):
DriverManager类 是管理一堆JDBC驱动服务的基本服务
//使用反射加载Driver
Class<?> aClass = Class.forName(“com.mysql.jdbc.Driver”),
Driver driver =(Driver)aClass.newInstance();
//创建url和user和password
String url =”jdbc:mysql://localhost:3306/hsp_db02″;
String user =”root”;String password =”hsp”;
DriverManager.registerDriver(driver);//注册Driver驱动 注册给定的驱动程序
Connection connection = DriverManager.getConnection(url, user,password);//这个调用这个方法直接传入参数
System.out.println(“第三种方式=”+ connection);
第四种方法(用的最多, 利用列加载让系统自动注册驱动):
Class.forName(“com.mysql.jdbc.Driver”); //这里利用反射,让类加载,类加载静态代码块也会加载,里面自动注册驱动
//创建url和user 和 password
String url=”jdbc:mysql://localhost:3306/hsp_db02″
String user = “root”;
String password = “hsp”;
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(“第4种方式”+ connection);
第五种方式(使用配置文件,在第四种上优化)
//通过Properties对象获取配置文件的信息
Properties properties = new Properties();
properties.load(new FileInputStream(“src\lmysql.properties”))//获取相关的值
String user = properties.getProperty(“user”);
String password = properties.getProperty(“password”),
String driver = properties.getProperty(“driver”);
String url = properties.getProperty(“url”);
Class.forName(driver)/建议写上
ResultSet[结果集,select的返回]
resultset 是 select查询返回的一个结果集
基本介绍
1.表示数据库结果集的数据表,通常通过执行查询数据库的语句生成
2.Resultset对象保持一个光标指向其当前的数据行。最初,光标位于第一行之前
3.next方法将光标移动到下一行,并且由于在ResultSet对象中没有更多行时返回false ,因此可以在while循环中使用循环来遍历结果集
代码:
String sql = “select id, name , sex, borndate from actor”;
//执行给定的 SQL 语句,该语句返回单个 ResultSet 对象
/*
+—-+———–+—–+———————+
| id | name | sex | borndate |
+—-+———–+—–+———————+——-+
| 4 | 刘德华 | 男 | 1970-12-12 00:00:00 |
| 5 | jack | 男 | 1990-11-11 00:00:00 |
+—-+———–+—–+———————+——-+
debug 代码 resultSet 对象的结构
*/
ResultSet resultSet = statement.executeQuery(sql);//执行mysql语句返回一个resultset对象
使用 while 取出数据
while (resultSet.next()) { // 让光标向后移动,如果没有更多行,则返回 false
int id = resultSet.getInt(1); //获取该行的第 1 列
int id1 = resultSet.getInt(“id”); 通过列名来获取值, 推荐
String name = resultSet.getString(2);//获取该行的第 2 列
String sex = resultSet.getString(3);
Date date = resultSet.getDate(4);
System.out.println(id + “\t” + name + “\t” + sex + “\t” + date);
底层是 rows 是一个arraylist集合 里面包含一个elementdata对象数组,对象数组里面包括了各行数据
resultset是一个接口,
statement(用来执行 静态的sql语句,并且返回的结果生成对象)
风险:
JDBC也是也是如此
暂无评论内容