语言概述和jdk安装与配置 1-1 软件开发介绍
软件开发 软件,即一系列按照特定顺序组织的计算机数据和指令的集合。有系统软件和应用软件之分。
人机交互方式 图形化界面 (Graphical User Interface GUI):这种方式简单直观,使用者易于接受,容易上手操作。命令行方式 (Command Line Interface CLI):需要有一个控制台,输入特定的指令,让计算机完成一些操作。较为麻烦,需要记录住一些命令。
常用的DOS命令 dir : 列出当前目录下的文件以及文件夹 md : 创建目录 rd : 删除目录 cd : 进入指定目录 cd… : 退回到上一级目录 cd: 退回到根目录 del : 删除文件 exit : 退出 dos 命令行 echo javase>1.doc:创建文件1.doc并写入javase
常用快捷键 ← →:移动光标 ↑ ↓:调阅历史操作命令 Delete和Backspace:删除字符
1-2 计算机编程语言介绍
什么是计算机语言 语言:是人与人之间用于沟通的一种方式。例如:中国人与中国人用普通话沟通。而中国人要和英国人交流,就要学习英语。 计算机语言:人与计算机交流的方式。 如果人要与计算机交流,那么就要学习计算机语言。 计算机语言有很多种。如:C ,C++ ,Java ,PHP , Kotlin,Python,Scala等。
第一代语言:机器语言。 指令以二进制代码形式存在。
第二代语言:汇编语言。 使用助记符表示一条机器指令。
第三代语言:高级语言 C、Pascal、Fortran面向过程的语言 C++面向过程/面向对象 Java跨平台的纯面向对象的语言 .NET跨语言的平台 Python、Scala…
1-3 Java语言概述
是SUN(Stanford University Network,斯坦福大学网络公司 ) 1995年推出的一门高级编程语言。
是一种面向Internet的编程语言。Java一开始富有吸引力是因为Java程序可以在Web浏览器中运行。这些Java程序被称为Java小程序(applet)。applet使用现代的图形用户界面与Web用户进行交互。 applet内嵌在HTML代码中。
随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。 后台开发:Java、PHP、Python、Go、Node.js
1.3.1 Java简史 1991年 Green项目,开发语言最初命名为Oak (橡树) 1994年,开发组意识到Oak 非常适合于互联网 1996年,发布JDK 1.0,约8.3万个网页应用Java技术来制作 1997年,发布JDK 1.1,JavaOne会议召开,创当时全球同类会议规模之最 1998年,发布JDK 1.2,同年发布企业平台J2EE 1999年,Java分成J2SE、J2EE和J2ME,JSP/Servlet技术诞生 2004年,发布里程碑式版本:JDK 1.5,为突出此版本的重要性,更名为JDK 5.0 2005年,J2SE -> JavaSE,J2EE -> JavaEE,J2ME -> JavaME 2009年,Oracle公司收购SUN,交易价格74亿美元 2011年,发布JDK 7.0 2014年,发布JDK 8.0,是继JDK 5.0以来变化最大的版本 2017年,发布JDK 9.0,最大限度实现模块化 2018年3月,发布JDK 10.0,版本号也称为18.3 2018年9月,发布JDK 11.0,版本号也称为18.9
1.3.2 Java技术体系平台
Java SE(Java Standard Edition)标准版 支持面向桌面级应用(如Windows下的应用程序)的Java平台,提供了完整的Java核 心API ,此版本以前称为J2SE。
Java EE(Java Enterprise Edition)企业版 是为开发企业环境下的应用程序提供的一套解决方案。该技术体系中包含的技术如:Servlet 、Jsp等,主要针对于Web应用程序开发。版本以前称为J2EE。
Java ME(Java Micro Edition)小型版 支持Java程序运行在移动终端(手机、PDA)上的平台,对Java API有所精简,并加入了针对移动终端的支持,此版本以前称为J2ME。
Java Card 支持一些Java小程序(Applets)运行在小内存设备(如智能卡)上的平台。
1.3.3 Java在各领域的应用
企业级应用:主要指复杂的大企业的软件系统、各种类型的网站。Java的安全机制以及它的跨平台的优势,使它在分布式系统领域开发中有广泛应用。应用领域包括金融、电信、交通、电子商务等。
Android平台应用:Android应用程序使用Java语言编写。Android开发水平的高低很大程度上取决于Java语言核心能力是否扎实。
大数据平台开发:各类框架有Hadoop,spark,storm,flink等,就这类技术生态圈来讲,还有各种中间件如flume,kafka,sqoop等等 ,这些框架以及工具大多数是用Java编写而成,但提供诸如Java,scala,Python,R等各种语言API供编程。
移动领域应用:主要表现在消费和嵌入式领域,是指在各种小型设备上的应用,包括手机、PDA、机顶盒、汽车通信设备等。
1.3.4 Java语言的诞生 java之父James Gosling团队在开发”Green”项目时,发现C缺少垃圾回收系统,还有可移植的安全性、分布程序设计和多线程功能。最后,他们想要一种易于移植到各种设备上的平台,于是Java语言诞生了。
1.3.4 Java语言的主要特性
Java语言是易学的。Java语言的语法与C语言和C++语言很接近,使得大多数程序员很容易学习和使用Java。
Java语言是强制面向对象的。Java语言提供类、接口和继承等原语,为了简单起见,只支持类之间的单继承,但支持接口之间的多继承,并支持类与接口之间的实现机制(关键字为implements)。
Java语言是分布式的。Java语言支持Internet应用的开发,在基本的Java应用编程接口中有一个网络应用编程接口(java net),它提供了用于网络应用编程的类库,包括URL、URLConnection、Socket、ServerSocket等。Java的RMI(远程 方法激活)机制也是开发分布式应用的重要手段。
Java语言是健壮的。Java的强类型机制、异常处理、垃圾的自动收集等是Java程序 健壮性的重要保证。对指针的丢弃是Java的明智选择。
Java语言是安全的。Java通常被用在网络环境中,为此,Java提供了一个安全机制以防恶意代码的攻击。如:安全防范机制(类ClassLoader),如分配不同的名字空间以防替代本地的同名类、字节代码检查。
Java语言是体系结构中立的。Java程序(后缀为java的文件)在Java平台上被编译为体系结构中立的字节码格式(后缀为class的文件),然后可以在实现这个Java平台的任何系统中运行。
Java语言是解释型的。如前所述,Java程序在Java平台上被编译为字节码格式,然后可以在实现这个Java平台的任何系统的解释器中运行。
Java是性能略高的。与那些解释型的高级脚本语言相比,Java的性能还是较优的。
Java语言是原生支持多线程的。在Java语言中,线程是一种特殊的对象,它必须由Thread类或其子(孙)类来创建。
1-4 Java程序运行机制及运行过程 1.4.1 Java语言的特点
特点一:面向对象 两个基本概念:类、对象 三大特性:封装、继承、多态
特点二:健壮性 吸收了C/C++语言的优点,但去掉了其影响程序健壮性的部分(如指针、内存的申请与释放等),提供了一个相对安全的内存管理和访问机制
特点三:跨平台性 跨平台性:通过Java语言编写的应用程序在不同的系统平台上都可以运行。“Write once , Run Anywhere” 原理:只要在需要运行 java 应用程序的操作系统上,先安装一个Java虚拟机 (JVM Java Virtual Machine) 即可。由JVM来负责Java程序在该系统中的运行。
1.4.2 Java两种核心机制
Java虚拟机 (Java Virtal Machine)
垃圾收集机制 (Garbage Collection)
①核心机制—Java虚拟机
JVM是一个虚拟的计算机,具有指令集并使用不同的存储区域。负责执行指令,管理数据、内存、寄存器。
对于不同的平台,有不同的虚拟机。
只有某平台提供了对应的java虚拟机,java程序才可在此平台运行
Java虚拟机机制屏蔽了底层运行平台的差别,实现了“一次编译,到处运行”。 ②核心机制—垃圾回收
不再使用的内存空间应回收—— 垃圾回收。 在C/C++等语言中,由程序员负责回收无用内存。 Java 语言消除了程序员回收无用内存空间的责任:它提供一种系统级线程跟踪存储空间的分配情况。并在JVM空闲时,检查并释放那些可被释放的存储空间。
垃圾回收在Java程序运行过程中自动进行,程序员无法精确控制和干预。
Java程序还会出现内存泄漏和内存溢出问题吗?Yes!
1-5 Java语言的环境搭建 1.5.1 什么是JDK,JRE?
JDK(Java Development Kit Java开发工具包) JDK是提供给Java开发人员使用的,其中包含了java的开发工具,也包括了JRE。所以安装了JDK,就不用在单独安装JRE了。 其中的开发工具:编译工具(javac.exe) 打包工具(jar.exe)等
JRE(Java Runtime Environment Java运行环境) 包括Java虚拟机(JVM Java Virtual Machine)和Java程序所需的核心类库等,如果想要运行一个开发好的Java程序,计算机中只需要安装JRE即可。
1.5.2 JDK、JRE、JVM关系
JDK = JRE + 开发工具集(例如Javac编译工具等)
JRE = JVM + Java SE标准类库
下载并安装JDK请参考:Java中JDK的下载安装与配置
1-6 开发体验— HelloWorld 步骤:
将 Java 代码编写到扩展名为 .java 的文件中。
1 2 3 4 5 6 public class Test { public static void main (String[] args) { System.out.println(“Hello World!”); } } 12345
通过 javac 命令对该 java 文件进行编译。 有了java源文件,通过编译器将其编译成JVM可以识别的字节码文件。在该源文件目录下,通过javac编译工具对Test.java文件进行编译。如果程序没有错误,没有任何提示,但在当前目录下会出现一个est.class文件,该文件称为字节码文件,也是可以执行的java的程序。
通过 java 命令对生成的 class 文件进行运行。
1-7 常见问题及解决方法 原因: 1.源文件名不存在或者写错 2.当前路径错误 3.后缀名隐藏问题 原因: 1.类文件名写错,尤其文件名与类名不一致时,要小心 2.类文件不在当前路径下,或者不在classpath指定路径下 原因:声明为public的类应与文件名一致,否知编译失败 原因:编译失败,注意错误出现的行数,再到源代码中指定位置改错总结: 学习编程最容易犯的错是语法错误。Java要求你必须按照语法规则编写代码。如果你的程序违反了语法规则,例如:忘记了分号、大括号、引号,或者拼错了单词,java编译器都会报语法错误。尝试着去看懂编译器会报告的错误信息。
1-8 注释
用于注解说明解释程序的文字就是注释。
Java中的注释类型: 单行注释: //注释文字 多行注释:/* 注释文字 */ 文档注释 (java特有) :
提高了代码的阅读性;调试程序的重要方法。
注释是一个程序员必须要具有的良好编程习惯。
将自己的思想通过注释先整理出来,再用代码去体现
注: ①对于单行和多行注释,被注释的文字,不会被JVM(java虚拟机)解释执行。 ②多行注释里面不允许有多行注释嵌套。 ③文档注释内容可以被JDK提供的工具 javadoc 所解析,生成一套以网页文件形式体现的该程序的说明文档。
1-9 Java API文档
API (Application Programming Interface,应用程序编程接口)是 Java 提供的基本编程接口。
Java语言提供了大量的基础类,因此 Oracle 也为这些基础类提供了相应的API文档,用于告诉开发者如何使用这些类,以及这些类里包含的方法。
下载API:Java API文档下载
1-10 良好的编程风格
正确的注释和注释风格 ①使用文档注释来注释整个类或整个方法。 ②如果注释方法中的某一个步骤,使用单行或多行注释。
正确的缩进和空白 ①使用一次tab操作,实现缩进 ②运算符两边习惯性各加一个空格。比如:2 + 4 * 5。
块的风格 Java API 源代码选择了行尾风格
1 2 3 4 5 6 public class Test { public static void main (String[] args) { System.out.println("Block Style!" ); } } 12345
推荐阅读:阿里巴巴编码规范
1-11 常用的Java开发工具 1.文本编辑工具:
记事本
UltraEdit
EditPlus
TextPad
NotePad
2.Java集成开发环境(IDE):
JBuilder
NetBeans
Eclipse
MyEclipse
STS
IntelliJ IDEA
java基本语法 1、关键字与标识符 关键字 1.定义:被Java语言赋予了特殊含义,用做专门用途的字符串 (单词) 2.特点:关键字中所有字母都为小写 3.官方地址:https://docs.oracle.com/[javase](https://so.csdn.net/so/search?q=javase&spm=1001.2101.3001.7020)/tutorial/java/nutsandbolts/_keywords.html
用于定义数据类型的关键字
class
interface
enum
byte
short
int
long
float
double
char
boolean
void
用于定义流程控制的关键字
if
else
switch
case
default
while
do
for
continue
continue
return
用于定义访问权限修饰符的关键字
private
protected
public
用于定义类,函数,变量修饰符的关键字
abstract
final
static
synchronized
用于定义类与类之间关系的关键字
extends
implements
用于定义建立实例及引用实例,判断实例的关键字
new
this
super
instanceof
用于异常处理的关键字
try
catch
finally
throw
throws
用于包的关键字
package
import
其他修饰符关键字
native
strictfp
transient
volatile
assert
用于定义数据类型值的字面值
true
false
null
保留字 Java保留字:现有Java版本尚未使用,但以后版本可能会作为关键字使用。自己命名标识符时要避免使用这些保留字(goto 、const)。
标识符 1.标识符: ①Java 对各种变量、方法和类等要素命名时使用的字符序列称为标识符 ②技巧:凡是自己可以起名字的地方都叫标识符。 2.定义合法标识符规则: ①由26个英文字母大小写,0-9 ,_或 $ 组成 ②数字不可以开头。 ③不可以使用关键字和保留字,但能包含关键字和保留字。 ④Java中严格区分大小写,长度无限制。 ⑤标识符不能包含空格。 2.Java中的名称命名规范: ①包名:多单词组成时所有字母都小写:xxxyyyzzz ②类名、接口名:多单词组成时,所有单词的首字母大写:XxxYyyZzz ③变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写:xxxYyyZzz ④常量名:所有字母都大写。多单词时每个单词用下划线连接:XXX_YYY_ZZZ
注意1:在起名字时,为了提高阅读性,要尽量有意义,“见名知意”。
注意2:java采用unicode字符集,因此标识符也可以使用汉字声明,但是不建议使用。
推荐阅读:阿里巴巴编码规范 、代码整洁之道
2、变量 变量的分类 1.1 按数据类型分类
详细说明:
整型:byte(1字节=8bit) \ short(2字节) \ int(4字节) \ long(8字节) ① byte范围:-128 ~ 127 ② 声明long型变量,必须以”l”或”L”结尾 ③ 通常,定义整型变量时,使用int型。 ④整型的常量,默认类型是:int型
浮点型:float(4字节) \ double(8字节) ① 浮点型,表示带小数点的数值 ② float表示数值的范围比long还大 ③ 定义float类型变量时,变量要以”f”或”F”结尾 ④ 通常,定义浮点型变量时,使用double型。 ⑤ 浮点型的常量,默认类型为:double
字符型:char (1字符=2字节) ① 定义char型变量,通常使用一对’’,内部只能写一个字符 ② 表示方式:1.声明一个字符 2.转义字符 3.直接使用 Unicode 值 来表示字符型常量
布尔型:boolean ① 只能取两个值之一:true 、 false ② 常常在条件判断、循环结构中使用
1.2 按声明的位置分类
定义变量的格式: 数据类型 变量名 = 变量值; 或 数据类型 变量名; 变量名 = 变量值;
变量使用的注意点: ① 变量必须先声明,后使用 ② 变量都定义在其作用域内。在作用域内,它是有效的。换句话说,出了作用域,就失效了 ③ 同一个作用域内,不可以声明两个同名的变量
基本数据类型变量间运算规则 4.1. 涉及到的基本数据类型:除了boolean之外的其他7种
4.2. 自动类型转换(只涉及7种基本数据类型) 结论:当容量小的数据类型的变量与容量大的数据类型的变量做运算时,结果自动提升为容量大的数据类型。 byte 、char 、short –> int –> long –> float –> double 特别的:当byte、char、short三种类型的变量做运算时,结果为int型 说明:此时的容量大小指的是,表示数的范围的大和小。比如:float容量要大于long的容量
4.3. 强制类型转换(只涉及7种基本数据类型):自动类型提升运算的逆运算。 1.需要使用强转符:() 2.注意点:强制类型转换,可能导致精度损失。
4.4. String与8种基本数据类型间的运算
String属于引用数据类型,翻译为:字符串
声明String类型变量时,使用一对””
String可以和8种基本数据类型变量做运算,且运算只能是连接运算:+
运算的结果仍然是String类型 避免: String s = 123;//编译错误 String s1 = “123”;//正确 int i = (int)s1;//编译错误
3、进制 关于进制
所有数字在计算机底层都以二进制形式存在。
对于整数,有四种表示方式: ①二进制(binary):0,1 ,满2进1.以0b或0B开头。 ②十进制(decimal):0-9 ,满10进1。 ③八进制(octal):0-7 ,满8进1. 以数字0开头表示。 ④十六进制(hex):0-9及A-F,满16进1. 以0x或0X开头表示。此处的A-F不区分大小写。如:0x21AF +1= 0X21B0
二进制
java整数常量默认是int类型,当用二进制定义整数时,其第32位是符号位;当是long类型时,二进制默认占64位,第64位是符号位.
二进制的整数有如下三种形式: ①原码:直接将一个数值换成二进制数。最高位是符号位 ②负数的反码:是对原码按位取反,只是最高位(符号位)确定为1。 ③负数的补码:其反码加1。
计算机以二进制补码的形式保存所有的整数。 ①正数的原码、反码、补码都相同 ②负数的补码是其反码+1
为什么要使用原码、反码、补码表示形式呢? 计算机辨别“符号位”显然会让计算机的基础电路设计变得十分复杂! 于是人们想出了将符号位也参与运算的方法. 我们知道, 根据运算法则减去一个正数等于加上一个负数, 即: 1-1 = 1 + (-1) = 0 , 所以机器可以只有加法而没有减法, 这样计算机运算的设计就更简单了。二进制->十进制 计算机底层都以补码的方式来存储数据! 对于正数来讲:原码、反码、补码是相同的:三码合一。 计算机底层都是使用二进制表示的数值 计算机底层都是使用的数值的补码保存数据的。
进制间的转化
4、运算符 算术运算符 算术运算符的注意问题 ①如果对负数取模,可以把模数负号忽略不记,如:5%-2=1。 但被模数是负数则不可忽略。此外,取模运算的结果不一定总是整数。 ②对于除号“/”,它的整数除和小数除是有区别的:整数之间做除法时,只保留整数部分而舍弃小数部分。 例如:int x=3510;x=x/1000*1000; //3000 ③“+”除字符串相加功能外,还能把非字符串转换成字符串.例如:System.out.println(“5+5=”+5+5); //打印结果是:5+5=55
赋值运算符 赋值运算符:= += -= *= /= %=
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int i2,j2; i2 = j2 = 10 ; int i3 = 10 ,j3 = 20 ; int num1 = 10 ; num1 += 2 ; System.out.println(num1); int num2 = 12 ; num2 %= 5 ; System.out.println(num2); short s1 = 10 ; s1 += 2 ; System.out.println(s1); 12345678910111213141516
说明: 1.运算的结果不会改变变量本身的数据类型
比较运算符 ①比较运算符的结果都是boolean型,也就是要么是true,要么是false。 ②比较运算符“==”不能误写成“=” 。 【典型代码】
1 2 3 4 5 6 7 8 9 10 11 int i = 10 ; int j = 20 ; System.out.println(i == j); System.out.println(i = j); boolean b1 = true ; boolean b2 = false ; System.out.println(b2 == b1); System.out.println(b2 = b1); 12345678910
【特别说明的】 1.比较运算符的结果是boolean类型 2.> < >= <= :只能使用在数值类型的数据之间。 \3. == 和 !=: 不仅可以使用在数值类型数据之间,还可以使用在其他引用类型变量之间。 Account acct1 = new Account(1000); Account acct2 = new Account(1000); boolean b1 = (acct1 == acct2);//比较两个Account是否是同一个账户。 boolean b2 = (acct1 != acct2);
逻辑运算符 【特别说明的】 1.逻辑运算符操作的都是boolean类型的变量。而且结果也是boolean类型 【典型代码】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 boolean b1 = true ; b1 = false ; int num1 = 10 ; if (b1 & (num1++ > 0 )){ System.out.println("我现在在北京" ); }else { System.out.println("我现在在南京" ); } System.out.println("num1 = " + num1); boolean b2 = true ; b2 = false ; int num2 = 10 ; if (b2 && (num2++ > 0 )){ System.out.println("我现在在北京" ); }else { System.out.println("我现在在南京" ); } System.out.println("num2 = " + num2); boolean b3 = false ; b3 = true ; int num3 = 10 ; if (b3 | (num3++ > 0 )){ System.out.println("我现在在北京" ); }else { System.out.println("我现在在南京" ); } System.out.println("num3 = " + num3); boolean b4 = false ; b4 = true ; int num4 = 10 ; if (b4 || (num4++ > 0 )){ System.out.println("我现在在北京" ); }else { System.out.println("我现在在南京" ); } System.out.println("num4 = " + num4); 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
位运算符 位运算是直接对整数的二进制进行的运算 【典型代码】
1 2 3 4 5 6 7 8 9 10 11 12 int i = 21 ; i = -21 ; System.out.println("i << 2 :" + (i << 2 )); System.out.println("i << 3 :" + (i << 3 )); System.out.println("i << 27 :" + (i << 27 )); int m = 12 ; int n = 5 ; System.out.println("m & n :" + (m & n)); System.out.println("m | n :" + (m | n)); System.out.println("m ^ n :" + (m ^ n)); 1234567891011
【特别说明的】 ① 位运算符操作的都是整型的数据 ② >> :在一定范围内,每向右移1位,相当于 / 2; << :在一定范围内,每向左移1位,相当于 * 2。
三元运算符 三元运算符:(条件表达式)? 表达式1 : 表达式2 【典型代码】 ①获取两个整数的较大值 ②获取三个数的最大值 【特别说明的】
说明 ① 条件表达式的结果为boolean类型 ② 根据条件表达式真或假,决定执行表达式1,还是表达式2. 如果表达式为true,则执行表达式1。 如果表达式为false,则执行表达式2。 ③ 表达式1 和表达式2要求是一致的。 ④ 三元运算符可以嵌套使用
凡是可以使用三元运算符的地方,都可以改写为if-else,反之,不成立。
如果程序既可以使用三元运算符,又可以使用if-else结构,那么优先选择三元运算符。原因:简洁、执行效率高。
5、流程控制 顺序结构
分支结构 1.if-else条件判断结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 结构一: if (条件表达式){ 执行表达式 } 结构二:二选一 if (条件表达式){ 执行表达式1 }else { 执行表达式2 } 结构三:n选一 if (条件表达式){ 执行表达式1 }else if (条件表达式){ 执行表达式2 }else if (条件表达式){ 执行表达式3 } ... else { 执行表达式n } 123456789101112131415161718192021222324
说明: ① else 结构是可选的。 ② 针对于条件表达式:
如果多个条件表达式之间是“互斥”关系(或没有交集的关系),哪个判断和执行语句声明在上面还是下面,无所谓。 如果多个条件表达式之间有交集的关系,需要根据实际情况,考虑清楚应该将哪个结构声明在上面。 如果多个条件表达式之间有包含的关系,通常情况下,需要将范围小的声明在范围大的上面。否则,范围小的就没机会执行了。
③if-else结构是可以相互嵌套的。 ④ 如果if-else结构中的执行语句只有一行时,对应的一对{}可以省略的。但是,不建议大家省略。
2.switch-case选择结构
1 2 3 4 5 6 7 8 9 10 11 12 13 switch (表达式){case 常量1 : 执行语句1 ; case 常量2 : 执行语句2 ; ... default : 执行语句n; } 123456789101112
说明: ① 根据switch表达式中的值,依次匹配各个case中的常量。一旦匹配成功,则进入相应case结构中,调用其执行语句。当调用完执行语句以后,则仍然继续向下执行其他case结构中的执行语句,直到遇到break关键字或此switch-case结构 末尾结束为止。 ② break,可以使用在switch-case结构中,表示一旦执行到此关键字,就跳出switch-case结构 ③ switch结构中的表达式,只能是如下的6种数据类型之一: byte 、short、char、int、枚举类型(JDK5.0新增)、String类型(JDK7.0新增) ④ case 之后只能声明常量。不能声明范围。 ⑤ break关键字是可选的。 ⑥ default:相当于if-else结构中的else. default结构是可选的,而且位置是灵活的。 3.如果switch-case结构中的多个case的执行语句相同,则可以考虑进行合并。 4.break在switch-case中是可选的
循环结构 1.循环结构的四要素 ① 初始化条件 ② 循环条件 —>是boolean类型 ③ 循环体 ④ 迭代条件 说明:通常情况下,循环结束都是因为②中循环条件返回false了。
2.三种循环结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 for (①;②;④){ ③ } ① while (②){ ③; ④; } ① do { ③; ④; }while (②); 123456789101112131415161718192021222324252627282930
3.“无限循环”结构: while(true) 或 for( ; ; ) 总结:如何结束一个循环结构? 方式一:当循环条件是false时 方式二:在循环体中,执行break
4.嵌套循环 4.1.嵌套循环:将一个循环结构A声明在另一个循环结构B的循环体中,就构成了嵌套循环 内层循环:循环结构A 外层循环:循环结构B 4.2.说明: ① 内层循环结构遍历一遍,只相当于外层循环循环体执行了一次 ② 假设外层循环需要执行m次,内层循环需要执行n次。此时内层循环的循环体一共执行了m * n次 ③ 外层循环控制行数,内层循环控制列数 【典型练习】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 for (int j = 1 ;j <= 4 ;j++ ){ for (int i = 1 ;i <= 6 ;i++){ System.out.print('*' ); } System.out.println(); } for (int i = 1 ;i <= 5 ;i++){ for (int j = 1 ;j <= i;j++){ System.out.print("*" ); } System.out.println(); } 12345678910111213141516171819202122232425262728293031
衡量一个功能代码的优劣: 1.正确性 2.可读性 3.健壮性 4.高效率与低存储:时间复杂度 、空间复杂度 (衡量算法的好坏)
4、break和continue关键字的使用
使用范围
循环中使用的作用(不同点)
相同点
break
switch-case 、循环结构中
结束当前循环
关键字后面不能声明执行语句
continue
循环结构中
结束当次循环
关键字后面不能声明执行语句
6、计算机字符编码 有关编码的基础知识 1、位 bit 最小的单元 字节 byte 机器语言的单位 1byte=8bits 1KB=1024byte 1MB=1024KB 1GB=1024MB 2、二进制 binary 八进制 octal 十进制 decimal 十六进制 hex 3、字符 :是各种文字和符号的总称,包括各个国家的文字,标点符号,图形符号,数字等。字符集: 字符集是多个符号的集合,每个字符集包含的字符个数不同。字符编码: 字符集只是规定了有哪些字符,而最终决定采用哪些字符,每一个字符用多少字节表示等问题,则是由编码来决定的。计算机要准确的处理各种字符集文字,需要进行字符编码,以便计算机能够识别和存储各种文字。
常见字符编码的介绍 首先来看一下常用的编码有哪些,截图自 Notepad++。其中 ANSI 在中国大陆即为 GBK(以前是 GB2312),最常用的是 GBK 和 UTF8 无 BOM 编码格式。后面三个都是有 BOM 头的文本格式,UCS-2 即为人们常说的 Unicode 编码,又分为大端、小端。 所谓 BOM 头(Byte Order Mark)就是文本文件中开始的几个并不表示任何字符的字节,用二进制编辑器(如 bz.exe)就能看到了。 UTF8 的 BOM 头为 0xEF 0xBB 0xBF Unicode 大端模式为 0xFE 0xFF Unicode 小端模式为 0xFF 0xFE 说明:需要添加插件 HEX-Editor
ASCII 码 计算机一开始发明的时候是用来解决数字计算的问题,后来人们发现,计算机还可以做更多的事,例如文本处理。但由于计算机只识“数”,因此人们必须告诉计算机哪个数字来代表哪个特定字符,例如 65 代表字母‘A’,66 代表字母‘B’,以此类推。但是计算机之间字符-数字的对应关系必须得一致,否则就会造成同一段数字在不同计算机上显示出来的字符不一样。因此美国国家标准协会 ANSI 制定了一个标准,规定了常用字符的集合以及每个字符对应的编号,这就是 ASCII 字符集Character Set),也称 ASCII 码。 那时候的字符编解码系统非常简单,就是简单的查表过程。例如将字符序列编码为二进制流写入存储设备,只需要在 ASCII 字符集中依次找到字符对应的字节,然后直接将该字节写入存储设备即可。解码二进制流的过程也是类似。 其中:
0~31 及 127(共 33 个)是控制字符或通信专用字符(其余为可显示字符),如控制符:LF(换行)、CR(回车)、FF(换页)、DEL(删除)、BS(退格)
32~126(共 95 个)是字符(32 是空格),其中 48~57 为 0 到 9 十个阿拉伯数字。
65~90 为 26 个大写英文字母,97~122 号为 26 个小写英文字母,其余为一些标点符号、运算符号等。
后 128 个称为扩展 ASCII 码。许多基于 x86 的系统都支持使用扩展(或“高”)ASCII。扩展ASCII 码允许将每个字符的第 8 位用于确定附加的 128 个特殊符号字符、外来语字母和图形符号。
OEM 字符集的衍生 当计算机开始发展起来的时候,人们逐渐发现,ASCII 字符集里那可怜的 128 个字符已经不能再满足他们的需求了。人们就在想,一个字节能够表示的数字(编号)有 256 个,而 ASCII 字符只用到了 x000x7F,也就是占用了前 128 个,后面 128 个数字不用白不用,因此很多人打起了后面这 128 个数字的主意。可是问题在于,很多人同时有这样的想法,但是大家对于0x80-0xFF 这后面的 128 个数字分别对应什么样的字符,却有各自的想法。这就导致了当时销往世界各地的机器上出现了大量各式各样的 OEM 字符集。 大家对于 0x000x7F 这个范围的解释基本是相同的,而对于后半部分 0x80~0xFF 的解释却不一定相同。甚至有时候同样的字符在不同 OEM 字符集中对应的字节也是不同的。 不同的 OEM 字符集导致人们无法跨机器交流各种文档。例如职员甲发了一封简历 résumés 给职员乙,结果职员乙看到的却是 r?sum?s,因为é字符在职员甲机器上的 OEM 字符集中对应的字节是 0x82,而在职员乙的机器上,由于使用的 OEM 字符集不同,对 0x82 字节解码后得到的字符却是?。
多字节字符集(MBCS)和中文字符集 上面我们提到的字符集都是基于单字节编码,也就是说,一个字节翻译成一个字符。这对于拉丁语系国家来说可能没有什么问题,因为他们通过扩展第8个比特,就可以得到256个字符了, 足够用了。但是对于亚洲国家来说,256 个字符是远远不够用的。因此这些国家的人为了用上电脑,又要保持和 ASCII 字符集的兼容,就发明了多字节编码方式,相应的字符集就称为多字节字符集(Muilti-Bytes Charecter Set)。例如中国使用的就是双字节字符集编码。 例如目前最常用的中文字符集 GB2312,涵盖了所有简体字符以及一部分其他字符;GBK(K 代表扩展的意思)则在 GB2312 的基础上加入了对繁体字符等其他非简体字符。这两个字符集的字符都是使用 1-2 个字节来表示。Windows 系统采用 936 代码页来实现对 GBK 字符集的编解码。在解析字节流的时候,如果遇到字节的最高位是 0 的话,那么就使用 936 代码页中的第 1 张码表进行解码,这就和单字节字符集的编解码方式一致了。如果遇到字节的最高位是 1 的话,那么就表示需要两个字节值才能对应一个字符。 假如你使用 GB2312 写了这么一句话: 我叫 ABC 它的二进制编码是这样的: 11001110 11010010 10111101 11010000 01000001 01000002 01000003 全角? 全角是一种电脑字符,且每个全角字符占用两个标准字符(或半角字符)位置。通常的英文字母、数字键、符号键都是半角的,半角的显示内码都是一个字节。为了排列整齐,英文和其它拉丁文的字符和标点也提供了全角格式。在中文输入法中,切换全角和半角格式的快捷键为SHIFT+空格。
ANSI 标准、国家标准、ISO 标准 不同 ASCII 衍生字符集的出现,让文档交流变得非常困难,因此各种组织都陆续进行了标准化流程。例如美国 ANSI 组织制定了 ANSI 标准字符编码(注意,我们现在通常说到 ANSI 编码,通常指的是平台的默认编码,例如英文操作系统中是 ISO-8859-1,中文系统是 GBK),ISO 组织制定的各种 ISO 标准字符编码,还有各国也会制定一些国家标准字符集,例如中国的 GBK,GB2312 和 GB18030。 操作系统在发布的时候,通常会往机器里预装这些标准的字符集还有平台专用的字符集,这样只要你的文档是使用标准字符集编写的,通用性就比较高了。例如你用 GB2312 字符集编写的文档,在中国大陆内的任何机器上都能正确显示。同时,我们也可以在一台机器上阅读多个国家不同语言的文档了,前提是本机必须安装该文档使用的字符集。
Unicode 的出现 虽然通过使用不同字符集,我们可以在一台机器上查阅不同语言的文档,但是我们仍然无法解决一个问题:如果一份文档中含有不同国家的不同语言的字符,那么无法在一份文档中显示所有字符。为了解决这个问题,我们需要一个全人类达成共识的巨大的字符集,这就是 Unicode字符集。Unicode 字符集涵盖了目前人类使用的所有字符,并为每个字符进行统一编号,分配唯一的字符码(Code Point)。Unicode 字符集将所有字符按照使用上的频繁度划分为 17 个层面(Plane),每个层面上有 216=65536 个字符码空间。 其中第 0 个层面 BMP,基本涵盖了当今世界用到的所有字符。其他的层面要么是用来表示一些远古时期的文字,要么是留作扩展。我们平常用到的 Unicode 字符,一般都是位于 BMP 层面上的。目前 Unicode 字符集中尚有大量字符空间未使用。
编码系统的变化 在 Unicode 出现之前,所有的字符集都是和具体编码方案绑定在一起的(即字符集≈编码方式),都是直接将字符和最终字节流绑定死了,例如 ASCII 编码系统规定使用 7 比特来编码 ASCII 字符集;GB2312 以及 GBK 字符集,限定了使用最多 2 个字节来编码所有字符,并且规定了字节序。这样的编码系统通常用简单的查表,也就是通过代码页就可以直接将字符映射为存储设备上的字节流了。例如下面这个例子: Unicode 同样也不完美,这里就有三个问题,一个是,我们已经知道,英文字母只用一个字节表示就够了,第二个问题是如何才能区别 Unicode 和 ASCII?计算机怎么知道两个字节表示一个符号,而不是分别表示两个符号呢?第三个,如果和 GBK 等双字节编码方式一样,用最高位是 1 或 0 表示两个字节和一个字节,就少了很多值无法用于表示字符,不够表示所有字符。Unicode 在很长一段时间内无法推广,直到互联网的出现,为解决 Unicode 如何在网络上传输的问题,于是面向传输的众多 UTF(UCS Transfer Format)标准出现了,顾名思义,UTF-8 就是每次 8 个位传输数据,而 UTF-16 就是每次 16 个位。UTF-8 就是在互联网上使用最广的一种Unicode 的实现方式,这是为传输而设计的编码,并使编码无国界,这样就可以显示全世界上所有文化的字符了。 UTF-8 最大的一个特点,就是它是一种变长的编码方式。它可以使用 1~4 个字节表示一个符号。从 unicode 到 uft-8 并不是直接的对应,而是要过一些算法和规则来转换(即 Uncidoe 字符集≠UTF-8 编码方式)。 Unicode 符号范围 | UTF-8 编码方式 (十六进制) | (二进制) —————————————————————– 0000 0000-0000 007F | 0xxxxxxx(兼容原来的 ASCII) 0000 0080-0000 07FF | 110xxxxx 10xxxxxx 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 因此,Unicode 只是定义了一个庞大的、全球通用的字符集,并为每个字符规定了唯一确定的编号,具体存储成什么样的字节流,取决于字符编码方案。推荐的 Unicode 编码是 UTF-16 和UTF-8。 早期字符编码、字符集和代码页等概念都是表达同一个意思。例如 GB2312 字符集、GB2312编码,936 代码页,实际上说的是同个东西。 但是对于 Unicode 则不同,Unicode 字符集只是定义了字符的集合和唯一编号,Unicode 编码,则是对 UTF-8、UCS-2/UTF-16 等具体编码方案的统称而已,并不是具体的编码方案。所以当需要用到字符编码的时候,你可以写 gb2312,codepage936,utf-8,utf-16,但请不要写 Unicode。 造成乱码的原因就是因为使用了错误的字符编码去解码字节流,因此当我们在思考任何跟文本显示有关的问题时,请时刻保持清醒:当前使用的字符编码是什么。只有这样,我们才能正确分析和处理乱码问题。常见 CharSet 有:GBK、GB2312、US-ASCII、ISO-8859-1、UTF-8、UTF-16BE、UTF-16LE、UTF-16
java中数组 一、数组的概述 1、数组(Array),是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理。 2、数组的常见概念 ① 数组名 ②下标(或索引) ③ 元素 ④数组的长度 3、 数组本身是引用数据类型,而数组中的元素可以是任何数据类型,包括基本数据类型和引用数据类型。 4、创建数组对象会在内存中开辟一整块连续的空间,而数组名中引用的是这块连续空间的首地址。 5、数组的长度一旦确定,就不能修改。 6、 我们可以直接通过下标(或索引)的方式调用指定位置的元素,速度很快。 7、数组的分类: ①按照维度:一维数组、二维数组 、三维数组、… ②按照元素的数据类型分:基本数据类型元素的数组、引用数据类型元素的数组(即对象数组)
二、一维数组 1、一维数组的声明 ①一维数组的声明方式: type var[] 或 type[] var; 例如: int a[]; int[] a1; double b[]; String[] c; //引用类型变量数组 ② Java语言中声明数组时不能指定其长度(数组中元素的数), 例如: int a[5]; //非法
2、一维数组的初始化
动态初始化:数组声明且为数组元素分配空间与赋值的操作分开进行
1 2 3 4 5 6 7 8 9 10 11 int [] arr = new int [3 ];arr[0 ] = 3 ; arr[1 ] = 9 ; arr[2 ] = 8 ; String names[]; names = new String [3 ]; names[0 ] = “钱学森”; names[1 ] = “邓稼先”; names[2 ] = “袁隆平”; 12345678910
静态初始化:在定义数组的同时就为数组元素分配空间并赋值。
1 2 3 4 int arr[] = new int []{ 3 , 9 , 8 };或 int [] arr = {3 ,9 ,8 };123
3、一维数组元素的引用
①定义并用运算符new为之分配空间后,才可以引用数组中的每个元素; ②数组元素的引用方式:数组名[数组元素下标]
数组元素下标可以是整型常量或整型表达式。如a[3] , b[i] , c[6*i];
数组元素下标从0开始;长度为n的数组合法下标取值范围: 0 —>n-1;如int a[]=new int[3]; 可引用的数组元素为a[0]、a[1]、a[2]
③每个数组都有一个属性length指明它的长度,例如:a.length 指明数组a的长度(元素个数)
4、一维数组数组元素的默认初始化值
数组是引用类型,它的元素相当于类的成员变量,因此数组一经分配空间,其中的每个元素也被按照成员变量同样的方式被隐式初始化。例如:
1 2 3 4 5 6 7 public class Test { public static void main (String argv[]) { int a[]= new int [5 ]; System.out.println(a[3 ]); } } 123456
对于基本数据类型而言,默认初始化值各有不同
对于引用数据类型而言,默认初始化值为null(注意与0不同!)5、一维数组的创建 Java中使用关键字new来创建数组,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Test { public static void main (String args[]) { int [] s; s = new int [10 ]; for ( int i=0 ; i<10 ; i++ ) { s[i] =2 *i+1 ; System.out.println(s[i]); } } } 12345678910111213
6、一维数组的内存解析
三、二维数组 1、如何理解二维数组? ①数组属于引用数据类型 ②数组的元素也可以是引用数据类型 ③一个一维数组A的元素如果还是一个一维数组类型的,则,此数组A称为二维数组。
2、二维数组的声明与初始化
1 2 3 4 5 6 7 8 9 10 11 int [] arr = new int []{1 ,2 ,3 };int [][] arr1 = new int [][]{{1 ,2 ,3 },{4 ,5 },{6 ,7 ,8 }};String[][] arr2 = new String [3 ][2 ]; String[][] arr3 = new String [3 ][]; int [] arr4[] = new int [][]{{1 ,2 ,3 },{4 ,5 ,9 ,10 },{6 ,7 ,8 }};int [] arr5[] = {{1 ,2 ,3 },{4 ,5 },{6 ,7 ,8 }};12345678910
3、如何调用二维数组元素?
1 2 3 4 5 6 7 System.out.println(arr1[0 ][1 ]); System.out.println(arr2[1 ][1 ]); arr3[1 ] = new String [4 ]; System.out.println(arr3[1 ][0 ]); System.out.println(arr3[0 ]); 123456
4、二维数组的属性:
1 2 3 4 System.out.println(arr4.length); System.out.println(arr4[0 ].length); System.out.println(arr4[1 ].length); 123
5、遍历二维数组元素
1 2 3 4 5 6 7 for (int i = 0 ;i < arr4.length;i++){ for (int j = 0 ;j < arr4[i].length;j++){ System.out.print(arr4[i][j] + " " ); } System.out.println(); } 123456
6、二维数组元素的默认初始化值
规定:二维数组分为外层数组的元素,内层数组的元素
int[][] arr = new int[4][3];
1
1 2 3 4 - ``` 外层元素:arr[0],arr[1]等 1
内层元素:arr[0][0],arr[1][2]等
1
1 2 3 4 5 6 针对于初始化方式一:比如:int[][] arr = new int[4][3]; - ``` 外层元素的初始化值为:地址值 1
内层元素的初始化值为:与一维数组初始化情况相同
1
1 2 3 4 5 6 针对于初始化方式二:比如:int[][] arr = new int[4][]; - ``` 外层元素的初始化值为:null 1
内层元素的初始化值为:不能调用,否则报错。
1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 **7、二维数组的内存解析**  ## 四、Arrays工具类的使用 **1.理解:** ① 定义在java.util包下。 ② Arrays:提供了很多操作数组的方法。 **2.使用:** ```java //1.boolean equals(int[] a,int[] b):判断两个数组是否相等。 int[] arr1 = new int[]{1,2,3,4}; int[] arr2 = new int[]{1,3,2,4}; boolean isEquals = Arrays.equals(arr1, arr2); System.out.println(isEquals); //2.String toString(int[] a):输出数组信息。 System.out.println(Arrays.toString(arr1)); //3.void fill(int[] a,int val):将指定值填充到数组之中。 Arrays.fill(arr1,10); System.out.println(Arrays.toString(arr1)); //4.void sort(int[] a):对数组进行排序。 Arrays.sort(arr2); System.out.println(Arrays.toString(arr2)); //5.int binarySearch(int[] a,int key) int[] arr3 = new int[]{-98,-34,2,34,54,66,79,105,210,333}; int index = Arrays.binarySearch(arr3, 210); if(index >= 0){ System.out.println(index); }else{ System.out.println("未找到"); } 12345678910111213141516171819202122232425
五、数组中常见的异常 1.数组角标越界异常:ArrayIndexOutOfBoundsException
1 2 3 4 5 6 7 8 int [] arr = new int []{1 ,2 ,3 ,4 ,5 }; for (int i = 0 ;i <= arr.length;i++){ System.out.println(arr[i]); } System.out.println(arr[-2 ]); System.out.println("hello" ); 1234567
2.空指针异常:NullPointerException
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int [] arr1 = new int []{1 ,2 ,3 }; arr1 = null ; System.out.println(arr1[0 ]); int [][] arr2 = new int [4 ][]; System.out.println(arr2[0 ][0 ]); String[] arr3 = new String []{"AA" ,"BB" ,"CC" }; arr3[0 ] = null ; System.out.println(arr3[0 ].toString()); 1234567891011121314
一旦程序出现异常,未处理时,就终止执行。
面向对象编程 面向对象之类与对象 面向过程与面向对象 1、面向过程(POP) 与 面向对象(OOP) ①二者都是一种思想,面向对象是相对于面向过程而言的。面向过程,强调的是功能行为,以函数为最小单位,考虑怎么做。面向对象,将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。 ② 面向对象更加强调运用人类在日常的思维逻辑中采用的思想方法与原则,如抽象、分类、继承、聚合、多态等。 2、面向对象的三大特征 ①封装 (Encapsulation) ②继承 (Inheritance) ③多态 (Polymorphism) 3、面向对象的思想概述 ①程序员从面向过程的执行者转化成了面向对象的指挥者 ②面向对象分析方法分析问题的思路和步骤:
根据问题需要,选择问题所针对的现实世界中的实体。
从实体中寻找解决问题相关的属性和功能,这些属性和功能就形成了概念世界中的类。
把抽象的实体用计算机语言进行描述,形成计算机世界中类的定义。即借助某种程序语言,把类构造成计算机能够识别和处理的数据结构。
将类实例化成计算机世界中的对象。对象是计算机世界中解决问题的最终工具。
Java语言的基本元素:类和对象 1、类:对一类事物的描述,是抽象的、概念上的定义 2、对象:是实际存在的该类事物的每个个体,因而也称为实例(instance)
面向对象程序设计的重点是类的设计 设计类,就是设计类的成员。
3、 类的语法格式
属 性:对应类中的成员变量
行 为:对应类中的成员方法
1 2 3 4 5 6 7 public class Person { private int age ; public void showAge (int i) { age = i; } } 123456
说明:
属性 = 成员变量 = field = 域、字段
方法 = 成员方法 = 函数 = method
创建类的对象 = 类的实例化 = 实例化类
4、创建Java自定义类 步骤: ① 定义类(考虑修饰符、类名) ② 编写类的属性(考虑修饰符、属性类型、属性名、初始化值) ③编写类的方法(考虑修饰符、返回值类型、方法名、形参等)
对象的创建和使用 1、创建对象语法: 类名 对象名 = new 类名(); 2、使用“对象名.对象成员”的方式访问对象成员(包括属性和方法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class Animal { public int legs; public void eat () { System.out.println(“Eating.”); } public viod move () { System.out.println(“Move.”); } } public class Zoo { public static void main (String args[]) { Animal xb=new Animal (); xb.legs=4 ; System.out.println(xb.legs); xb.eat(); xb.move(); } } 1234567891011121314151617181920
3、类的访问机制: ①在一个类中的访问机制:类中的方法可以直接访问类中的成员变量。(例外:static方法访问非static,编译不通过。) ②在不同类中的访问机制:先创建要访问类的对象,再用对象访问类中定义的成员。
4、对象的创建
1 2 3 4 5 Person p1 = new Person ();Person p2 = new Person ();Person p3 = p1;1234
说明: 如果创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性。(非static的) 意味着:如果我们修改一个对象的属性a,则不影响另外一个对象属性a的值。 5、对象的内存解析 6、匿名对象 匿名对象:我们创建的对象,没显式的赋给一个变量名。即为匿名对象。 特点:匿名对象只能调用一次。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 new Phone ().sendEmail(); new Phone ().playGame(); new Phone ().price = 1999 ; new Phone ().showPrice(); 12345 PhoneMall mall = new PhoneMall ();mall.show(new Phone ()); class PhoneMall { public void show (Phone phone) { phone.sendEmail(); phone.playGame(); } } 123456789101112
8、理解”万事万物皆对象” ①在Java语言范畴中,我们都将功能、结构等封装到类中,通过类的实例化,来调用具体的功能结构
Scanner,String等
文件:File
网络资源:URL
②涉及到Java语言与前端Html、后端的数据库交互时,前后端的结构在Java层面交互时,都体现为类、对象。
类的三大结构:属性、方法、构造器 类的成员之一:属性(field) 1、语法格式:
修饰符 数据类型 属性名 = 初始化值 ;
说明1: 修饰符
常用的权限修饰符有:private、缺省、protected、public
其他修饰符:static、final (暂不考虑)
说明2:数据类型
任何基本数据类型(如int、Boolean) 或 任何引用数据类型。
说明3:属性名
1 2 3 4 5 public class Person { private int age; public String name = “Lila”; } 1234
2、变量的分类:成员变量与局部变量
在方法体外,类体内声明的变量称为成员变量。
在方法体内部声明的变量称为局部变量。注意 :二者在初始化值方面的异同: 同:都有生命周期 异:局部变量除形参外,均需显式初始化。
3、成员变量(属性)和局部变量的区别?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Person { String name; int age = 1 ; boolean isMale; public void show (String nation) { String color; color = "yellow" ; } } class PersonTest { public static void main (String[] args) { Person p = new Person (); p.show(“USA”); } } 123456789101112131415161718
4、对象属性的默认初始化赋值
类的成员之二:方法(method) 1、什么是方法(method、函数):
方法是类或对象行为特征的抽象,用来完成某个功能操作。在某些语言中也称为函数或过程。
将功能封装为方法的目的是,可以实现代码重用,简化代码
Java里的方法不能独立存在,所有的方法必须定义在类里。
1 2 3 4 5 6 7 8 9 10 public class Person { private int age; public int getAge () { return age; } public void setAge (int i) { age = i; } } 123456789
2、方法的声明格式:
修饰符 返回值类型 方法名(参数类型 形参1, 参数类型 形参2, ….){ 方法体程序代码 return 返回值; }
其中: 修饰符:public,缺省,private, protected等 返回值类型:
没有返回值:void。
有返回值,声明出返回值的类型。与方法体中“return 返回值”搭配使用
方法名:属于标识符,命名时遵循标识符命名规则和规范,“见名知意” 形参列表:可以包含零个,一个或多个参数。多个参数时,中间用“,”隔开 返回值:方法在执行完毕后返还给调用它的程序的数据。
3、方法的分类:按照是否有形参及返回值
4、方法的调用
方法通过方法名被调用,且只有被调用才会执行。
方法调用的过程分析注 意:
方法被调用一次,就会执行一次
没有具体返回值的情况,返回值类型用关键字void表示,那么方法体中可以不必使用return语句。如果使用,仅用来结束方法。
定义方法时,方法的结果应该返回给调用者,交由调用者处理。
方法中只能调用方法或属性,不可以在方法内部定义方法。
5、对象数组的内存解析
6、方法的重载 ①重载的概念 在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。 ②重载的特点: 与返回值类型无关,只看参数列表,且参数列表必须不同。(参数个数或参数类型)。调用时,根据方法参数列表的不同来区别。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int add (int x,int y) {return x+y;}int add (int x,int y,int z) {return x+y+z;}double add (double x,double y) {return x+y;}123456 public class PrintStream { public static void print (int i) {……} public static void print (float f) {……} public static void print (String s) {……} public static void main (String[] args) { print(3 ); print(1.2f ); print("hello!" ); } } 12345678910
使用重载方法,可以为编程带来方便。
例如,System.out.println()方法就是典型的重载方法,其内部的声明形式如下:
1 2 3 4 5 6 7 8 9 10 11 public void println (byte x) public void println (short x) public void println (int x) public void println (long x) public void println (float x) public void println (double x) public void println (char x) public void println (double x) public void println () …… 12345678910
7、可变个数的形参 JavaSE 5.0 中提供了Varargs(variable number of arguments)机制,允许直接定义能和多个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可变的实参。
1 2 3 4 5 public static void test (int a ,String[] books) ;public static void test (int a ,String…books) ;1234
说明: ① 声明格式:方法名(参数的类型名 …参数名) ② 可变参数:方法参数部分指定类型的参数个数是可变多个:0个,1个或多个 ③ 可变个数形参的方法与同名的方法之间,彼此构成重载 ④ 可变参数方法的使用与方法参数部分使用数组是一致的 ⑤ 方法的参数部分有可变形参,需要放在形参声明的最后 ⑥ 在一个方法的形参位置,最多只能声明一个可变个数形参
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public void test (String[] msg) { System.out.println(“含字符串数组参数的test方法 "); } public void test1(String book){ System.out.println(“****与可变形参方法构成重载的test1方法****" );} public void test1 (String ... books) { System.out.println("****形参长度可变的test1方法****" ); } public static void main (String[] args) { TestOverload to = new TestOverload (); to.test1(); to.test1("aa" , "bb" ); to.test(new String []{"aa" }); } 1234567891011121314151617
8、方法参数的值传递机制 ① 方法,必须由其所在类或对象调用才有意义。若方法含有参数:
形参:方法声明时的参数
实参:方法调用时实际传给形参的参数值
② Java的实参值如何传入方法呢? Java里方法的参数传递方式只有一种:值传递。 即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响。
形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参
形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参
基本数据类型的参数传递 引用数据类型的参数传递
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public static void main (String[] args) { Person obj = new Person (); obj.age = 5 ; System.out.println("修改之前age = " + obj.age); change(obj); System.out.println("修改之后age = " + obj.age); } public static void change (Person obj) { System.out.println("change:修改之前age = " + obj.age); obj.age = 3 ; System.out.println("change:修改之后age = " + obj.age); } class Person { int age; } 1234567891011121314151617
经典题目:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class TransferTest3 { public static void main (String args[]) { TransferTest3 test = new TransferTest3 (); test.first(); } public void first () { int i = 5 ; Value v = new Value (); v.i = 25 ; second(v, i); System.out.println(v.i); } public void second (Value v, int i) { i = 0 ; v.i = 20 ; Value val = new Value (); v = val; System.out.println(v.i + " " + i); } } class Value { int i = 15 ; } 1234567891011121314151617181920212223
内存解析: 打印结果:
15 0 20
9、递归(recursion)方法
递归方法:一个方法体内调用它自身。
方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。
递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环。
1 2 3 4 5 6 7 8 9 public int sum (int num) { if (num == 1 ){ return 1 ; }else { return num + sum(num - 1 ); } } 12345678
斐波那契数列、不死神兔、n的阶乘等问题等可以用递归方法解决!
类的成员之三:构造器(或构造方法) 1、构造器的特征
它具有与类相同的名称
它不声明返回值类型。(与声明为void不同)
不能被static、final、synchronized、abstract、native修饰,不能有return语句返回值
2、构造器的作用 作用:创建对象;给对象进行初始化
如:Order o = new Order(); Person p = new Person(“Peter”,15);
3、语法格式
修饰符 类名 (参数列表) { 初始化语句; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Animal { private int legs; public Animal () { legs = 4 ; } public void setLegs (int i) { legs = i; } public int getLegs () { return legs; } } 12345678910111213
创建Animal类的实例:Animal a = new Animal(); 调用构造器,将legs初始化为4。
4、分类 根据参数不同,构造器可以分为如下两类:
隐式无参构造器(系统默认提供)
显式定义一个或多个构造器(无参、有参)注 意:
Java语言中,每个类都至少有一个构造器
默认构造器的修饰符与所属类的修饰符一致
一旦显式定义了构造器,则系统不再提供默认构造器
一个类可以创建多个重载的构造器
父类的构造器不可被子类继承
5、构造器重载
1 2 3 4 5 6 7 8 class Person { String name; int age; public Person (String n , int a) { name=n; age=a; } } 1234567
构造器重载使得对象的创建更加灵活,方便创建各种不同的对象。
1 2 3 4 5 6 7 public class Person { public Person (String name, int age, Date d) {this (name,age);…} public Person (String name, int age) {…} public Person (String name, Date d) {…} public Person () {…} } 123456
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class Person { private String name; private int age; private Date birthDate; public Person (String n, int a, Date d) { name = n; age = a; birthDate = d; } public Person (String n, int a) { name = n; age = a; } public Person (String n, Date d) { name = n; birthDate = d; } public Person (String n) { name = n; age = 30 ; } } 12345678910111213141516171819202122
6、JavaBean ①JavaBean是一种Java语言写成的可重用组件。 ②所谓javaBean,是指符合如下标准的Java类:
类是公共的
有一个无参的公共的构造器
有属性,且有对应的get、set方法
用户可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以用Java代码创造的对象进行打包,并且其他的开发者可以通过内部的JSP页面、Servlet、其他JavaBean、applet程序或者应用来使用这些对象。用户可以认为JavaBean提供了一种随时随地的复制和粘贴的功能,而不用关心任何改变。举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class JavaBean { private String name; private int age; public JavaBean () { } public int getAge () { return age; } public void setAge (int a) { age = a; } public String getName () { return name; } public void setName (String n) { name = n; } } 123456789101112131415161718
7、UML类图
总结 1、 赋值的位置: ① 默认初始化 ② 显式初始化 ③ 构造器中初始化 ④ 通过“对象.属性“或“对象.方法”的方式赋值 2、 赋值的先后顺序: ① - ② - ③ - ④
面向对象三大特征:封装、继承、多态 面向对象特征之一:封装和隐藏 1、使用者对类内部定义的属性(对象的成员变量)的直接操作会导致数据的错误、混乱或安全性问题。 2、信息的封装和隐藏 Java中通过将数据声明为私有的(private ),再提供公共的(public)方法:getXxx()和setXxx()实现对该属性的操作,以实现下述目的:
隐藏一个类中不需要对外提供的实现细节;
使用者只能通过事先定制好的方法来访问数据,可以方便地加入控制逻辑,限制对属性的不合理操作;
便于修改,增强代码的可维护性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Animal { private int legs; public void setLegs (int i) { if (i != 0 && i != 2 && i != 4 ) { System.out.println("Wrong number of legs!" ); return ; } legs = i; } public int getLegs () { return legs; } } public class Zoo { public static void main (String args[]) { Animal xb = new Animal (); xb.setLegs(4 ); System.out.println(xb.getLegs()); } } 123456789101112131415161718192021
3、四种访问权限修饰符
面向对象特征之二:继承性 1、为什么要有继承?
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
2、 此处的多个类称为子类(派生类),单独的这个类称为父类(基类 或超类)。可以理解为:“子类 is a 父类” 3、类继承语法规则:
class Subclass extends SuperClass{ }
①为描述和处理个人信息,定义类Person: ②为描述和处理学生信息,定义类Student: ③通过继承,简化Student类的定义:
Student类继承了父类Person的所有属性和方法,并增加了一个属性school。Person中的属性和方法,Student都可以使用。
4、作用:
继承的出现减少了代码冗余,提高了代码的复用性。
继承的出现,更有利于功能的扩展。
继承的出现让类与类之间产生了关系,提供了多态的前提。
注意:
不要仅为了获取其他类中某个功能而去继承
子类继承了父类,就继承了父类的方法和属性。
在子类中,可以使用父类中定义的方法和属性,也可以创建新的数据和方法。
在Java 中,继承的关键字用的是“extends”,即子类不是父类的子集,而是对父类的“扩展”。
5、关于继承的规则:
子类不能直接访问父类中私有的(private)的成员变量和方法。
6、 Java只支持单继承和多层继承,不允许多重继承
一个子类只能有一个父类
一个父类可以派生出多个子类
方法的重写 1、定义:在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。 2、要求: ①子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表 ②子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型 ③ 子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限
④子类方法抛出的异常不能大于父类被重写方法的异常注意: 子类与父类中同名同参数的方法必须同时声明为非static的(即为重写),或者同时声明为static的(不是重写)。因为static方法是属于类的,子类无法覆盖父类的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class Person { public String name; public int age; public String getInfo () { return "Name: " + name + "\n" +"age: " + age; } } public class Student extends Person { public String school; public String getInfo () { return "Name: " + name + "\nage: " + age + "\nschool: " + school; } public static void main (String args[]) { Student s1=new Student (); s1.name="Bob" ; s1.age=20 ; s1.school="school2" ; System.out.println(s1.getInfo()); Person p1=new Person (); p1.getInfo(); s1.getInfo(); } } 123456789101112131415161718192021222324252627
面向对象特征之三:多态性 1、多态性,是面向对象中最重要的概念,在Java中的体现:对象的多态性:父类的引用指向子类的对象
2、Java引用变量有两个类型:编译时类型和运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。简称:编译时,看左边;运行时,看右边。
若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism)
多态情况下,“看左边”:看的是父类的引用(父类中不具备子类特有的方法) “看右边”:看的是子类的对象(实际运行的是子类重写父类的方法)
3、对象的多态 —在Java中,子类的对象可以替代父类的对象使用
一个变量只能有一种确定的数据类型
一个引用类型变量可能指向(引用)多种不同类型的对象
Person p = new Student(); Object o = new Person();//Object类型的变量o,指向Person类型的对象 o = new Student(); //Object类型的变量o,指向Student类型的对象
4、子类可看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向上转型(upcasting)。 5、一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中添加的属性和方法
Student m = new Student(); m.school = “pku”; //合法,Student类有school成员变量 Person e = new Student(); e.school = “pku”; //非法,Person类没有school成员变量
属性是在编译时确定的,编译时e为Person类型,没有school成员变量,因而编译错误。 6、方法声明的形参类型为父类类型,可以使用子类的对象作为实参调用该方法 7、虚拟方法调用(多态情况下) 子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。
Person e = new Student(); e.getInfo(); //调用Student类的getInfo()方法 编译时类型和运行时类型:编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo()方法。——动态绑定
方法的重载与重写小结 从编译和运行的角度看: 重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不 同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了 不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类 和子类的,即子类可以重载父类的同名不同参数的方法。 所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”; 而对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。 引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,它就不是多态。”多态小结 ① 多态作用:
②前提:
③ 成员方法:
编译时:要查看引用变量所声明的类中是否有所调用的方法。
运行时:调用实际new的对象所属的类中的重写方法。
④ 成员变量:
类与类之间的关系 依赖关系(Dependency) 对象之间最弱的一种关联方式,是临时性的关联。代码中一般指由局部变量、函数参数、返回值建立的对于其他对象的调用关系。
1 2 3 4 5 6 7 8 9 10 class A { public B method (C c,D d) { E e = new E (); ... B b = new B (); ... return b; } } 123456789
这个代码结构中,表示 A 类依赖了 B,C,D,E 类
关联关系(Association) 对象之间一种引用关系,比如客户类与订单类之间的关系。这种关系通常使用类的属性表达。关联可以有方向,即导航。一般不作说明的时候,导航是双向的,不需要在线上标出箭头。 大部分情况下导航是单向的,可以加一个箭头表示。
1 2 3 4 5 6 7 8 9 class Employee { private int eid; private String name; private Computer coumputer; } class Computer {} 12345678
Employee——>Computer
1 2 3 4 5 6 7 class Husband { private Wife wife; } class Wife { private Husband husband; } 123456
Husband——Wife
关联表示类之间的“持久”关系,这种关系一般表示一种重要的业务之间的关系,需要保存的,或者说需要“持久化”的,或者说需要保存到数据库中的。另外,依赖表示类之间的是 一种“临时、短暂”关系,这种关系是不需要保存的。
聚合(Aggregation) 聚合(关联关系的一种):表示 has-a 的关系。与关联关系一样,聚合关系也是通过实例变量来实现这样关系的。关联关系和聚合关系来语法上是没办法区分的,从语义上才能更好的区分两者的区别。 如汽车类与引挚类,轮胎类之间的关系就是整体与个体的关系。 与关联关系一样,聚合关系也是通过实例变量来实现的。
1 2 3 4 5 class Car { private Engine engine; private Tyre[] tyres; } 1234
关联和聚集(聚合)的区别: 关联关系所涉及的两个对象是处在同一个层次上的。比如人和自行车就是一种关联关系,而不是聚合关系,因为人不是由自行车组成的。 聚合关系涉及的两个对象处于不平等的层次上,一个代表整体,一个代表部分。比如电脑和它的显示器、键盘、主板以及内存就是聚集关系,因为主板是电脑的组成部分。
组合(Composite) 对象 A 包含对象 B,对象 B 离开对象 A 没有实际意义。是一种更强的关联关系。人包含手,手离开人的躯体就失去了它应有的作用。 组合:表示 contains-a 的关系,是一种强烈的包含关系。组合类负责被组合类的生命周期。也使用属性表达组合关系,是关联关系的一种,是比聚合关系强的关系。
1 2 3 4 5 6 class Window { private Menu menu; private Slider slider; private Panel panel; } 12345
继承(Generalization) 又称为泛化,is-a 的关系 类与类的继承关系,类与接口的实现关系。 场景:父与子、动物与人、植物与树
抽象类与接口 抽象类与抽象方法 随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。 1、用abstract 关键字来修饰一个类,这个类叫做抽象类。 2、用abstract来修饰一个方法,该方法叫做抽象方法。
抽象方法:只有方法的声明,没有方法的实现。以分号结束,比如:public abstract void talk();
3、 含有抽象方法的类必须被声明为抽象类。 4、 抽象类不能被实例化。抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类。 5、 不能用abstract修饰变量、代码块、构造器; 6、 不能用abstract修饰私有方法、静态方法、final的方法、final的类。注意 :抽象类不能实例化 new Vihicle()是非法的 7、多态的应用:模板方法设计模式(TemplateMethod) 抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。 解决的问题:
当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。
设计模式:模板方法 (TemplateMethod)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 abstract class Template { public final void getTime () { long start = System.currentTimeMillis(); code(); long end = System.currentTimeMillis(); System.out.println("执行时间是:" + (end - start)); } public abstract void code () ; } class SubTemplate extends Template { public void code () { for (int i = 0 ; i < 10000 ; i++) { System.out.println(i); } } } 12345678910111213141516
模板方法设计模式是编程中经常用得到的模式。各个框架、类库中都有他的影子,比如常见的有:
数据库访问的封装
Junit单元测试
JavaWeb的Servlet中关于doGet/doPost方法调用
Hibernate中模板程序
Spring中JDBCTemlate、HibernateTemplate等
接口(interface) 1、概述
一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。
另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有is-a的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、MP3机、手机、数码相机、移动硬盘等都支持USB连接。
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要…则必须能…”的思想。继承是一个”是不是”的关系,而接口实现则是 “能不能”的关系。
接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都要遵守。 2、 接口(interface)是抽象方法和常量值定义的集合。
接口的特点: ①用interface来定义。 ②接口中的所有成员变量都默认是由public static final修饰的。 ③接口中的所有抽象方法都默认是由public abstract修饰的。 ④接口中没有构造器。 ⑤接口采用多继承机制。
1 2 3 4 5 6 7 public interface Runner {int ID = 1 ;void start () ;public void run () ;void stop () ;} 123456
上述例子相当于:
1 2 3 4 5 6 7 public interface Runner { public static final int ID = 1 ; public abstract void start () ; public abstract void run () ; public abstract void stop () ; } 123456
定义Java类的语法格式:先写extends,后写implements
class SubClass extends SuperClass implements InterfaceA{ }
一个类可以实现多个接口,接口也可以继承其它接口。
实现接口的类中必须提供接口中所有方法的具体实现内容,方可实例化。否则,仍为抽象类。
接口的主要用途就是被实现类实现。(面向接口编程)
与继承关系类似,接口与实现类之间存在多态性
接口和类是并列关系,或者可以理解为一种特殊的类。从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义 (JDK7.0及之前),而没有变量和方法的实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 interface Runner { public void start () ; public void run () ; public void stop () ; } class Person implements Runner { public void start () { } public void run () { } public void stop () { } } 123456789101112131415161718
3、代理模式(Proxy) 概述:代理模式是Java开发中使用较多的一种设计模式。代理设计就是为其他对象提供一种代理以控制对这个对象的访问。 ①应用场景:
安全代理:屏蔽对真实角色的直接访问。
远程代理:通过代理类处理远程方法调用(RMI)
延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象,比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打开文件时,不可能将所有的图片都显示出来,这样就可以使用代理模式,当需要查看图片时,用proxy来进行大图片的打开。
②分类
静态代理(静态定义代理类)
动态代理(动态生成代理类)
抽象类与接口的异同 面试题:排错 解析:子类继承B实现A中都有变量x,此时子类打印x时不知道到底是打印A的还是B的,所以应指定:
修改为: System.out.println(A.x); 或者 System.out.println(super.x);
Java 8中关于接口的改进 Java 8中,你可以为接口添加静态方法和默认方法。从技术角度来说,这是完全合法的,只是它看起来违反了接口作为一个抽象定义的理念。 1、静态方法:使用 static 关键字修饰。可以通过接口直接调用静态方法,并执行其方法体。我们经常在相互一起使用的类中使用静态方法。你可以在标准库中找到像Collection/Collections或者Path/Paths这样成对的接口和类。 2、默认方法:默认方法使用 default 关键字修饰。可以通过实现类对象来调用。我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。 比如:java 8 API中对Collection、List、Comparator等接口提供了丰富的默认方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 public interface AA { double PI = 3.14 ; public default void method () { System.out.println("北京" ); } default String method1 () { return "上海" ; } public static void method2 () { System.out.println(“hello lambda!"); } } 123456789101112
3、接口中的默认方法 ①若一个接口中定义了一个默认方法,而另外一个接口中也定义了一个同名同参数的方法(不管此方法是否是默认方法),在实现类同时实现了这两个接 口时,会出现:接口冲突。
解决办法:实现类必须覆盖接口中同名同参数的方法,来解决冲突。 ②若一个接口中定义了一个默认方法,而父类中也定义了一个同名同参数的非抽象方法,则不会出现冲突问题。因为此时遵守:类优先原则。接口中具有 相同名称和参数的默认方法会被忽略。
java中的结构和关键字 关键字:package、import 1、package语句作为Java源文件的第一条语句,指明该文件中定义的类所在的包。(若缺省该语句,则指定为无名包)。它的格式为: package 顶层包名.子包名 ; 举例:
1 2 3 4 5 6 7 8 package pack1.pack2; public class PackageTest { public void display () { System.out.println("in method display()" ); } } 1234567
2、包对应于文件系统的目录,package语句中,用 “.” 来指明包(目录)的层次; 3、包通常用小写单词标识。通常使用所在公司域名的倒置:com.baidu.xxx 4、包的作用:
包帮助管理大型软件系统:将功能相近的类划分到同一个包中。比如:MVC的设计模式
包可以包含类和子包,划分项目层次,便于管理
解决类命名冲突的问题
控制访问权限
5、MVC设计模式 6、JDK中主要的包介绍
关键字:final 1、 ①1. final修饰类
1 2 3 4 5 final class A {} class B extends A { } 1234
②final修饰方法
1 2 3 4 5 6 7 8 9 10 11 class A { public final void print () { System.out.println("A" ); } } class B extends A { public void print () { System.out.println("面向对象编程" ); } } 12345678910
③ final修饰变量——常量
1 2 3 4 5 6 7 8 class A { private final String INFO = "java" ; public void print () { } } 1234567
常量名要大写,内容不可修改。static final:全局常量 2、举例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public final class Test { public static int totalNumber = 5 ; public final int ID; public Test () { ID = ++totalNumber; } public static void main (String[] args) { Test t = new Test (); System.out.println(t.ID); final int I = 10 ; final int J; J = 20 ; J = 30 ; } } 1234567891011121314
Object类的使用 1、 2、Object类中的主要结构 3、==操作符与equals方法 4、toString() 方法
包装类的使用 总结:基本类型、包装类与String类间的转换 举例:
main()方法的使用 1、理解main方法的语法 ① 由于Java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public,又因为Java虚拟机在执行main()方法时不必创建对象,所以该方法必须 是static的,该方法接收一个String类型的数组参数,该数组中保存执行Java命令时传递给所运行的类的参数。 ② 又因为main() 方法是静态的,我们不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员,这种情况,我们在之前的例子中多次碰到。
类的成员之四:代码块 静态初始化块举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Person { public static int total; static { total = 100 ; System.out.println("in static block!" ); } } public class PersonTest { public static void main (String[] args) { System.out.println("total = " + Person.total); System.out.println("total = " + Person.total); } } 12345678910111213
输出: in static block total=100 total=100
类的内部成员之五:内部类 举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class Outer { private int s; public class Inner { public void mb () { s = 100 ; System.out.println("在内部类Inner中s=" + s); } } public void ma () { Inner i = new Inner (); i.mb(); } } public class InnerTest { public static void main (String args[]) { Outer o = new Outer (); o.ma(); } } 12345678910111213141516171819 public class Outer { private int s = 111 ; public class Inner { private int s = 222 ; public void mb (int s) { System.out.println(s); System.out.println(this .s); System.out.println(Outer.this .s); } public static void main (String args[]) { Outer a = new Outer (); Outer.Inner b = a.new Inner (); b.mb(333 ); } } 123456789101112131415
native 关键字的理解 使用 native 关键字说明这个方法是原生函数,也就是这个方法是用 C/C++等非Java 语言实现的,并且被编译成了 DLL,由 java 去调用。
1、为什么要用 native 方法? java 使用起来非常方便,然而有些层次的任务用 java 实现起来不容易,或者我们对程序的效率很在意时,问题就来了。例如:有时 java 应用需要与 java 外面的环境交互。这是本地方法存在的主要原因,你可以想想 java 需要与一些底层系统如操作系统或某些硬件交换信息时的情况。本地方法正是这样一种交流机制:它为我们提供了一个非常简洁的接口,而且我们无需去了解 java 应用之外的繁琐的细节。
2、native 声明的方法,对于调用者,可以当做和其他 Java 方法一样使用 一个 native method 方法可以返回任何 java 类型,包括非基本类型,而且同样可以进行异常控制。
native method 的存在并不会对其他类调用这些本地方法产生任何影响,实际上调用这些方法的其他类甚至不知道它所调用的是一个本地方法。JVM 将控制调用本地方法的所有细节。
如果一个含有本地方法的类被继承,子类会继承这个本地方法并且可以用 java语言重写这个方法(如果需要的话)。
java中异常 一、异常概述与异常体系结构 在使用计算机语言进行项目开发的过程中,即使程序员把代码写得尽善尽美,在系统的运行过程中仍然会遇到一些问题,因为很多问题不是靠代码能够避免的,比如:客户输入数据的格式,读取文件是否存在,网络是否始终保持通畅等等。
二、常见异常 常见异常:ArrayIndexOutOfBoundsException 常见异常:NullPointerException 常见异常:ArithmeticException 常见异常:ClassCastException
三、异常处理机制 一:try-catch-finally 举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class IndexOutExp { public static void main (String[] args) { String friends[] = { "lisa" , "bily" , "kessy" }; try { for (int i = 0 ; i < 5 ; i++) { System.out.println(friends[i]); } } catch (ArrayIndexOutOfBoundsException e) { System.out.println("index err" ); } System.out.println("\nthis is the end" ); } } 12345678910111213
程序IndexOutExp.java运行结果:java IndexOutExp lisa bily kessy index err this is the end
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class DivideZero1 { int x; public static void main (String[] args) { int y; DivideZero1 c = new DivideZero1 (); try { y = 3 / c.x; } catch (ArithmeticException e) { System.out.println("divide by zero error!" ); } System.out.println("program ends ok!" ); } } 12345678910111213
程序DivideZero1运行结果:java DivideZero1 divide by zero error! program ends ok!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class IOExp { public static void main (String[] args) { try { FileInputStream in = new FileInputStream ("atguigushk.txt" ); int b; b = in.read(); while (b != -1 ) { System.out.print((char ) b); b = in.read(); } in.close(); } catch (IOException e) { System.out.println(e); } finally { System.out.println(" It’s ok!" ); } } } 1234567891011121314151617
五、手动抛出异常
六、用户自定义异常类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class MyExpTest { public void regist (int num) throws MyException { if (num < 0 ) throw new MyException ("人数为负值,不合理" , 3 ); else System.out.println("登记人数" + num); } public void manager () { try { regist(100 ); } catch (MyException e) { System.out.print("登记失败,出错种类" + e.getId()); } System.out.print("本次登记操作结束" ); } public static void main (String args[]) { MyExpTest t = new MyExpTest (); t.manager(); } } 1234567891011121314151617181920
一首小悟结束异常处理
世界上最遥远的距离,是我在if里你在else里,似乎一直相伴又永远分离; 世界上最痴心的等待,是我当case你是switch,或许永远都选不上自己; 世界上最真情的相依,是你在try我在catch。无论你发神马脾气,我都默 默承受,静静处理。到那时,再来期待我们的finally
java中的多线程 一、基本概念:程序、进程、线程
二、线程的创建和使用
四、线程的同步 问题的提出 多个线程执行的不确定性引起执行结果的不稳定 多个线程对账本的共享,会造成操作的不完整性,会破坏数据。
模拟火车站售票程序,开启三个窗口售票。
五、线程的通信 经典生产者和消费者问题
六、JDK5.0新增线程创建方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class NumThread implements Callable { @Override public Object call () throws Exception { int sum = 0 ; for (int i = 1 ; i <= 100 ; i++) { if (i % 2 == 0 ){ System.out.println(i); sum += i; } } return sum; } } public class ThreadNew { public static void main (String[] args) { NumThread numThread = new NumThread (); FutureTask futureTask = new FutureTask (numThread); new Thread (futureTask).start(); try { Object sum = futureTask.get(); System.out.println("总和为:" + sum); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } 123456789101112131415161718192021222324252627282930313233343536373839
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 class NumberThread implements Runnable { @Override public void run () { for (int i = 0 ;i <= 100 ;i++){ if (i % 2 == 0 ){ System.out.println(Thread.currentThread().getName() + ": " + i); } } } } class NumberThread1 implements Runnable { @Override public void run () { for (int i = 0 ;i <= 100 ;i++){ if (i % 2 != 0 ){ System.out.println(Thread.currentThread().getName() + ": " + i); } } } } public class ThreadPool { public static void main (String[] args) { ExecutorService service = Executors.newFixedThreadPool(10 ); ThreadPoolExecutor service1 = (ThreadPoolExecutor) service; service.execute(new NumberThread ()); service.execute(new NumberThread1 ()); service.shutdown(); } }
java中的常用类 一、字符串相关的类 1、字符串String的介绍 String对象的创建:
1 2 3 4 5 6 7 8 9 String str = "hello" ;String s1 = new String (); String s2 = new String (String original); String s3 = new String (char [] a); String s4 = new String (char [] a,int startIndex,int count);12345678
字符串对象的存储
2、String常用方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 String str = "12hello34world5java7891mysql456" ;String string = str.replaceAll("\\d+" , "," ).replaceAll("^,|,$" , "" );System.out.println(string); String str = "12345" ;boolean matches = str.matches("\\d+" );System.out.println(matches); String tel = "0571-4534289" ;boolean result = tel.matches("0571-\\d{7,8}" );System.out.println(result); String str = "hello|world|java" ;String[] strs = str.split("\\|" ); for (int i = 0 ; i < strs.length; i++) {System.out.println(strs[i]); } System.out.println(); String str2 = "hello.world.java" ;String[] strs2 = str2.split("\\." ); for (int i = 0 ; i < strs2.length; i++) {System.out.println(strs2[i]); } 1234567891011121314151617181920212223242526
3、String与基本数据类型转换
1 2 3 4 5 6 7 8 9 String str = "中" ;System.out.println(str.getBytes("ISO8859-1" ).length); System.out.println(str.getBytes("GBK" ).length); System.out.println(str.getBytes("UTF-8" ).length); System.out.println(new String (str.getBytes("ISO8859-1" ), "ISO8859-1" ));System.out.println(new String (str.getBytes("GBK" ), "GBK" )); System.out.println(new String (str.getBytes("UTF-8" ), "UTF-8" )); 12345678
4、StringBuffer类
5、StringBuilder类 【面试题】程序输出:
1 2 3 4 5 6 7 8 String str = null ;StringBuffer sb = new StringBuffer ();sb.append(str); System.out.println(sb.length()); System.out.println(sb); StringBuffer sb1 = new StringBuffer (str);System.out.println(sb1); 1234567
二、JDK8之前日期时间API
1、java.lang.System类
2、java.util.Date类
1 2 3 4 5 6 7 8 Date date = new Date ();System.out.println(date); System.out.println(System.currentTimeMillis()); System.out.println(date.getTime()); Date date1 = new Date (date.getTime());System.out.println(date1.getTime()); System.out.println(date1.toString()); 1234567
3、java.text.SimpleDateFormat类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Date date = new Date (); SimpleDateFormat formater = new SimpleDateFormat ();System.out.println(formater.format(date)); SimpleDateFormat formater2 = new SimpleDateFormat ("yyyy年MM月dd日 EEE HH:mm:ss" );System.out.println(formater2.format(date)); try {Date date2 = formater2.parse("2008年08月08日 星期一 08:08:08" );System.out.println(date2.toString()); } catch (ParseException e) { e.printStackTrace(); } 123456789101112131415
4、 java.util.Calendar(日历)类
1 2 3 4 5 6 7 8 9 10 11 12 Calendar calendar = Calendar.getInstance();Date date = calendar.getTime();date = new Date (234234235235L ); calendar.setTime(date); calendar.set(Calendar.DAY_OF_MONTH, 8 ); System.out.println("当前时间日设置为8后,时间是:" + calendar.getTime()); calendar.add(Calendar.HOUR, 2 ); System.out.println("当前时间加2小时后,时间是:" + calendar.getTime()); calendar.add(Calendar.MONTH, -2 ); System.out.println("当前日期减2个月后,时间是:" + calendar.getTime());
一、JDK8中新日期时间API 1、背景
2、LocalDate、LocalTime、LocalDateTime
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 @Test public void test1 () { LocalDate localDate = LocalDate.now(); LocalTime localTime = LocalTime.now(); LocalDateTime localDateTime = LocalDateTime.now(); System.out.println(localDate); System.out.println(localTime); System.out.println(localDateTime); LocalDateTime localDateTime1 = LocalDateTime.of(2020 , 10 , 6 , 13 , 23 , 43 ); System.out.println(localDateTime1); System.out.println(localDateTime.getDayOfMonth()); System.out.println(localDateTime.getDayOfWeek()); System.out.println(localDateTime.getMonth()); System.out.println(localDateTime.getMonthValue()); System.out.println(localDateTime.getMinute()); LocalDate localDate1 = localDate.withDayOfMonth(22 ); System.out.println(localDate); System.out.println(localDate1); LocalDateTime localDateTime2 = localDateTime.withHour(4 ); System.out.println(localDateTime); System.out.println(localDateTime2); LocalDateTime localDateTime3 = localDateTime.plusMonths(3 ); System.out.println(localDateTime); System.out.println(localDateTime3); LocalDateTime localDateTime4 = localDateTime.minusDays(6 ); System.out.println(localDateTime); System.out.println(localDateTime4); } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
3、 瞬时:Instant
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Test public void test2 () { Instant instant = Instant.now(); System.out.println(instant); OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8 )); System.out.println(offsetDateTime); long milli = instant.toEpochMilli(); System.out.println(milli); Instant instant1 = Instant.ofEpochMilli(1550475314878L ); System.out.println(instant1); } 1234567891011121314151617181920212223
4、格式化与解析日期或时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 @Test public void test3 () { DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME; LocalDateTime localDateTime = LocalDateTime.now(); String str1 = formatter.format(localDateTime); System.out.println(localDateTime); System.out.println(str1); TemporalAccessor parse = formatter.parse("2019-02-18T15:42:18.797" ); System.out.println(parse); DateTimeFormatter formatter1 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG); String str2 = formatter1.format(localDateTime); System.out.println(str2); DateTimeFormatter formatter2 = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM); String str3 = formatter2.format(LocalDate.now()); System.out.println(str3); DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss" ); String str4 = formatter3.format(LocalDateTime.now()); System.out.println(str4); TemporalAccessor accessor = formatter3.parse("2019-02-18 03:52:09" ); System.out.println(accessor); } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
5、其他API
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 Set<String> zoneIds = ZoneId.getAvailableZoneIds(); for (String s : zoneIds) { System.out.println(s); } LocalDateTime localDateTime = LocalDateTime.now(ZoneId.of("Asia/Tokyo" ));System.out.println(localDateTime); ZonedDateTime zonedDateTime = ZonedDateTime.now();System.out.println(zonedDateTime); ZonedDateTime zonedDateTime1 = ZonedDateTime.now(ZoneId.of("Asia/Tokyo" ));System.out.println(zonedDateTime1); 12345678910111213141516 LocalTime localTime = LocalTime.now();LocalTime localTime1 = LocalTime.of(15 , 23 , 32 );Duration duration = Duration.between(localTime1, localTime);System.out.println(duration); System.out.println(duration.getSeconds()); System.out.println(duration.getNano()); LocalDateTime localDateTime = LocalDateTime.of(2016 , 6 , 12 , 15 , 23 , 32 );LocalDateTime localDateTime1 = LocalDateTime.of(2017 , 6 , 12 , 15 , 23 , 32 );Duration duration1 = Duration.between(localDateTime1, localDateTime);System.out.println(duration1.toDays()); 123456789101112 LocalDate localDate = LocalDate.now();LocalDate localDate1 = LocalDate.of(2028 , 3 , 18 );Period period = Period.between(localDate, localDate1);System.out.println(period); System.out.println(period.getYears()); System.out.println(period.getMonths()); System.out.println(period.getDays()); Period period1 = period.withYears(2 );System.out.println(period1); 12345678910 TemporalAdjuster temporalAdjuster = TemporalAdjusters.next(DayOfWeek.SUNDAY);LocalDateTime localDateTime = LocalDateTime.now().with(temporalAdjuster);System.out.println(localDateTime); LocalDate localDate = LocalDate.now().with(new TemporalAdjuster () {@Override public Temporal adjustInto (Temporal temporal) {LocalDate date = (LocalDate) temporal;if (date.getDayOfWeek().equals(DayOfWeek.FRIDAY)) {return date.plusDays(3 );} else if (date.getDayOfWeek().equals(DayOfWeek.SATURDAY)) { return date.plusDays(2 );} else { return date.plusDays(1 );} } }); System.out.println("下一个工作日是:" + localDate); 12345678910111213141516171819
6、与传统日期处理的转换
1、方式一:自然排序:java.lang.Comparable
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class Goods implements Comparable {private String name;private double price;@Override public int compareTo (Object o) { if (o instanceof Goods) { Goods other = (Goods) o; if (this .price > other.price) { return 1 ; } else if (this .price < other.price) { return -1 ; } return 0 ; } throw new RuntimeException ("输入的数据类型不一致" ); } } public class ComparableTest { public static void main (String[] args) { Goods[] all = new Goods [4 ]; all[0 ] = new Goods ("《红楼梦》" , 100 ); all[1 ] = new Goods ("《西游记》" , 80 ); all[2 ] = new Goods ("《三国演义》" , 140 ); all[3 ] = new Goods ("《水浒传》" , 120 ); Arrays.sort(all); System.out.println(Arrays.toString(all)); } } 12345678910111213141516171819202122232425262728293031
2、方式二:定制排序:java.util.Comparator
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Goods[] all = new Goods [4 ]; all[0 ] = new Goods ("War and Peace" , 100 ); all[1 ] = new Goods ("Childhood" , 80 ); all[2 ] = new Goods ("Scarlet and Black" , 140 ); all[3 ] = new Goods ("Notre Dame de Paris" , 120 ); Arrays.sort(all, new Comparator () { @Override public int compare (Object o1, Object o2) {Goods g1 = (Goods) o1;Goods g2 = (Goods) o2;return g1.getName().compareTo(g2.getName());} }); System.out.println(Arrays.toString(all)); 1234567891011121314
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 String javaVersion = System.getProperty("java.version" );System.out.println("java的version:" + javaVersion); String javaHome = System.getProperty("java.home" );System.out.println("java的home:" + javaHome); String osName = System.getProperty("os.name" );System.out.println("os的name:" + osName); String osVersion = System.getProperty("os.version" );System.out.println("os的version:" + osVersion); String userName = System.getProperty("user.name" );System.out.println("user的name:" + userName); String userHome = System.getProperty("user.home" );System.out.println("user的home:" + userHome); String userDir = System.getProperty("user.dir" );System.out.println("user的dir:" + userDir); 1234567891011121314
四、 Math类
五、 BigInteger与BigDecimal
1 2 3 4 5 6 7 8 9 10 @Test public void test2 () { BigInteger bi = new BigInteger ("1243324112234324324325235245346567657653" ); BigDecimal bd = new BigDecimal ("12435.351" ); BigDecimal bd2 = new BigDecimal ("11" ); System.out.println(bi); System.out.println(bd.divide(bd2, BigDecimal.ROUND_HALF_UP)); System.out.println(bd.divide(bd2, 25 , BigDecimal.ROUND_HALF_UP)); }
java中枚举与注解 一、枚举类的使用
1、 自定义枚举类 1 2 3 4 5 6 7 8 9 10 11 12 13 class Season { private final String SEASONNAME; private final String SEASONDESC; private Season (String seasonName,String seasonDesc) { this .SEASONNAME = seasonName; this .SEASONDESC = seasonDesc; } public static final Season SPRING = new Season ("春天" , "春暖花开" ); public static final Season SUMMER = new Season ("夏天" , "夏日炎炎" ); public static final Season AUTUMN = new Season ("秋天" , "秋高气爽" ); public static final Season WINTER = new Season ("冬天" , "白雪皑皑" ); } 123456789101112
2、使用enum 定义枚举类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public enum SeasonEnum { SPRING("春天" ,"春风又绿江南岸" ), SUMMER("夏天" ,"映日荷花别样红" ), AUTUMN("秋天" ,"秋水共长天一色" ), WINTER("冬天" ,"窗含西岭千秋雪" ); private final String seasonName; private final String seasonDesc; private SeasonEnum (String seasonName, String seasonDesc) { this .seasonName = seasonName; this .seasonDesc = seasonDesc; } public String getSeasonName () { return seasonName; } public String getSeasonDesc () { return seasonDesc; } } 123456789101112131415161718
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 public class SeasonTest1 { public static void main (String[] args) { Season1 summer = Season1.SUMMER; System.out.println(summer.toString()); System.out.println("****************" ); Season1[] values = Season1.values(); for (int i = 0 ;i < values.length;i++){ System.out.println(values[i]); values[i].show(); } System.out.println("****************" ); Thread.State[] values1 = Thread.State.values(); for (int i = 0 ; i < values1.length; i++) { System.out.println(values1[i]); } Season1 winter = Season1.valueOf("WINTER" ); System.out.println(winter); winter.show(); } } interface Info { void show () ; } enum Season1 implements Info { SPRING("春天" ,"春暖花开" ){ @Override public void show () { System.out.println("春天在哪里?" ); } }, SUMMER("夏天" ,"夏日炎炎" ){ @Override public void show () { System.out.println("宁夏" ); } }, AUTUMN("秋天" ,"秋高气爽" ){ @Override public void show () { System.out.println("秋天不回来" ); } }, WINTER("冬天" ,"冰天雪地" ){ @Override public void show () { System.out.println("大约在冬季" ); } }; private final String seasonName; private final String seasonDesc; private Season1 (String seasonName,String seasonDesc) { this .seasonName = seasonName; this .seasonDesc = seasonDesc; } public String getSeasonName () { return seasonName; } public String getSeasonDesc () { return seasonDesc; } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
二、注解(Annotation) 1、概述
2、常见的Annotation示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.annotation.javadoc;public class JavadocTest {public static void main (String[] args) {} public static double getArea (double radius) { return Math.PI * radius * radius; } } 12345678910111213141516171819202122
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.annotation.javadoc;public class AnnotationTest { public static void main (String[] args) { @SuppressWarnings("unused") int a = 10 ; } @Deprecated public void print () { System.out.println("过时的方法" ); } @Override public String toString () { return "重写的toString方法()" ; } } 123456789101112131415
1 2 3 4 5 6 7 8 9 <servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping> 12345678
3、 自定义 Annotation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @MyAnnotation(value = "java注解") public class MyAnnotationTest { public static void main (String[] args) { Class clazz = MyAnnotationTest.class; Annotation a = clazz.getAnnotation(MyAnnotation.class); MyAnnotation m = (MyAnnotation) a; String info = m.value(); System.out.println(info); } } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @interface MyAnnotation { String value () default "java Annotation" ; } 12345678910111213141516
4、JDK中的元注解
①@Retention
1 2 3 4 5 6 7 8 9 10 11 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { RetentionPolicy value () ; } 12345678910
举例:
1 2 3 4 @Retention(RetentionPolicy.SOURCE) @interface MyAnnotation1{ }@Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation2{ }123
②@Target
③@Documented和@Inherited
5、利用反射获取注解信息
6、JDK8中注解的新特性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @MyAnnotation public class AnnotationTest <U> { @MyAnnotation private String name; public static void main (String[] args) { AnnotationTest<@MyAnnotation String> t = null ; int a = (@MyAnnotation int ) 2L ; @MyAnnotation int b = 10 ; } public static <@MyAnnotation T> void method (T t) { } public static void test (@MyAnnotation String arg) throws @MyAnnotation Exception { } } @Target(ElementType.TYPE_USE) @interface MyAnnotation { }
集合 一、 Java集合框架概述
2、Map接口继承树
二、Collection接口方法
四、Collection子接口之一:List接口
五、Collection子接口之二:Set接口
1 2 3 4 5 6 7 8 9 10 11 12 @Override public int hashCode () { int result; long temp; result = id; result = 31 * result + (name != null ? name.hashCode() : 0 ); result = 31 * result + age; temp = Double.doubleToLongBits(salary); result = 31 * result + (int ) (temp ^ (temp >>> 32 )); return result; } 1234567891011
结果: [Person{id=1002, name=‘BB’}, Person{id=1001, name=‘CC’}] [Person{id=1002, name=‘BB’}, Person{id=1001, name=‘CC’}, Person{id=1001, name=‘CC’}] [Person{id=1002, name=‘BB’}, Person{id=1001, name=‘CC’}, Person{id=1001, name=‘CC’}, Person{id=1001, name=‘AA’}]
六、Map接口 常用方法: HashMap的底层实现原理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 public class TreeMapTest { @Test public void test1 () { TreeMap map = new TreeMap (); User u1 = new User ("Tom" ,23 ); User u2 = new User ("Jerry" ,32 ); User u3 = new User ("Jack" ,20 ); User u4 = new User ("Rose" ,18 ); map.put(u1,98 ); map.put(u2,89 ); map.put(u3,76 ); map.put(u4,100 ); Set entrySet = map.entrySet(); Iterator iterator1 = entrySet.iterator(); while (iterator1.hasNext()){ Object obj = iterator1.next(); Map.Entry entry = (Map.Entry) obj; System.out.println(entry.getKey() + "---->" + entry.getValue()); } } @Test public void test2 () { TreeMap map = new TreeMap (new Comparator () { @Override public int compare (Object o1, Object o2) { if (o1 instanceof User && o2 instanceof User){ User u1 = (User)o1; User u2 = (User)o2; return Integer.compare(u1.getAge(),u2.getAge()); } throw new RuntimeException ("输入的类型不匹配!" ); } }); User u1 = new User ("Tom" ,23 ); User u2 = new User ("Jerry" ,32 ); User u3 = new User ("Jack" ,20 ); User u4 = new User ("Rose" ,18 ); map.put(u1,98 ); map.put(u2,89 ); map.put(u3,76 ); map.put(u4,100 ); Set entrySet = map.entrySet(); Iterator iterator1 = entrySet.iterator(); while (iterator1.hasNext()){ Object obj = iterator1.next(); Map.Entry entry = (Map.Entry) obj; System.out.println(entry.getKey() + "---->" + entry.getValue()); } } } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
七、Collections工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @Test public void test2 () { List list = new ArrayList (); list.add(123 ); list.add(43 ); list.add(765 ); list.add(-97 ); list.add(0 ); List dest = Arrays.asList(new Object [list.size()]); System.out.println(dest.size()); Collections.copy(dest,list); System.out.println(dest); List list1 = Collections.synchronizedList(list); } 12345678910111213141516171819202122232425262728293031
hashmap底层实现原理 HashMap的底层实现原理
JDK 1.8之前
JDK 1.8
泛型 一、泛型的理解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Test public void test1 () { ArrayList list = new ArrayList (); list.add(78 ); list.add(76 ); list.add(89 ); list.add(88 ); for (Object score : list){ int stuScore = (Integer) score; System.out.println(stuScore); } } 123456789101112131415161718192021
二、在集合中使用泛型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 ArrayList<Integer> list = new ArrayList <>(); list.add(78 ); list.add(88 ); list.add(77 ); list.add(66 ); Iterator<Integer> iterator = list.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } 123456789101112131415 Map<String,Integer> map = new HashMap <String,Integer>(); map.put("Tom1" ,34 ); map.put("Tom2" ,44 ); map.put("Tom3" ,33 ); map.put("Tom4" ,32 ); Set<Entry<String,Integer>> entrySet = map.entrySet(); Iterator<Entry<String,Integer>> iterator = entrySet.iterator(); while (iterator.hasNext()){ Entry<String,Integer> entry = iterator.next(); System.out.println(entry.getKey() + "--->" + entry.getValue()); } 12345678910111213
三、自定义泛型结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class GenericTest { public static void main (String[] args) { ArrayList list = new ArrayList (); list.add("hello" ); test(list); } public static void test (ArrayList<String> list) { String str = "" ; for (String s : list) { str += s + "," ; } System.out.println("元素:" + str); } } 123456789101112131415161718
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 class Father <T1, T2> {} class Son1 extends Father {} class Son2 extends Father <Integer, String> {} class Son3 <T1, T2> extends Father <T1, T2> {} class Son4 <T2> extends Father <Integer, T2> {} 12345678910111213141516 class Father <T1, T2> {} class Son <A, B> extends Father {} class Son2 <A, B> extends Father <Integer, String> {} class Son3 <T1, T2, A, B> extends Father <T1, T2> {} class Son4 <T2, A, B> extends Father <Integer, T2> {} 12345678910111213141516 class Person <T> {private T info;public T getInfo () { return info; } public void setInfo (T info) { this .info = info; } public Person () {} public Person (T info) { this .info = info; } } 1234567891011121314151617181920212223242526272829
四、 泛型在继承上的体现
五、通配符的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 @Test public void test3 () { List<Object> list1 = null ; List<String> list2 = null ; List<?> list = null ; list = list1; list = list2; List<String> list3 = new ArrayList <>(); list3.add("AA" ); list3.add("BB" ); list3.add("CC" ); list = list3; list.add(null ); Object o = list.get(0 ); System.out.println(o); } public void print (List<?> list) { Iterator<?> iterator = list.iterator(); while (iterator.hasNext()){ Object obj = iterator.next(); System.out.println(obj); } } 123456789101112131415161718192021222324252627282930313233343536373839404142
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 @Test public void test4 () { List<? extends Person > list1 = null ; List<? super Person> list2 = null ; List<Student> list3 = new ArrayList <Student>(); List<Person> list4 = new ArrayList <Person>(); List<Object> list5 = new ArrayList <Object>(); list1 = list3; list1 = list4; list2 = list4; list2 = list5; list1 = list3; Person p = list1.get(0 ); list2 = list4; Object obj = list2.get(0 ); 编译不通过 list2.add(new Person ()); list2.add(new Student ()); }
io流
1 2 3 4 File file1 = new File ("d:\\java\\info.txt" );File file2 = new File ("d:" + File.separator + "java" + File.separator + "info.txt" );File file3 = new File ("d:/java" );123
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 File dir1 = new File ("D:/IOTest/dir1" );if (!dir1.exists()) { dir1.mkdir(); } File dir2 = new File (dir1, "dir2" );if (!dir2.exists()) { dir2.mkdirs(); } File dir4 = new File (dir1, "dir3/dir4" );if (!dir4.exists()) {dir4.mkdirs(); } File file = new File (dir2, "test.txt" );if (!file.exists()) { file.createNewFile(); } 123456789101112131415161718
二、IO流原理及流的分类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 public class FileInputOutputStreamTest { @Test public void testFileInputStream () { FileInputStream fis = null ; try { File file = new File ("hello.txt" ); fis = new FileInputStream (file); byte [] buffer = new byte [5 ]; int len; while ((len = fis.read(buffer)) != -1 ){ String str = new String (buffer,0 ,len); System.out.print(str); } } catch (IOException e) { e.printStackTrace(); } finally { if (fis != null ){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } } @Test public void testFileInputOutputStream () { FileInputStream fis = null ; FileOutputStream fos = null ; try { File srcFile = new File ("晚霞.jpg" ); File destFile = new File ("晚霞2.jpg" ); fis = new FileInputStream (srcFile); fos = new FileOutputStream (destFile); byte [] buffer = new byte [5 ]; int len; while ((len = fis.read(buffer)) != -1 ){ fos.write(buffer,0 ,len); } } catch (IOException e) { e.printStackTrace(); } finally { if (fos != null ){ try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if (fis != null ){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384 public class FileReaderWriterTest { public static void main (String[] args) { File file = new File ("hello.txt" ); System.out.println(file.getAbsolutePath()); File file1 = new File ("java\\hello.txt" ); System.out.println(file1.getAbsolutePath()); } @Test public void testFileReader () { FileReader fr = null ; try { File file = new File ("hello.txt" ); fr = new FileReader (file); int data; while ((data = fr.read()) != -1 ){ System.out.print((char )data); } } catch (IOException e) { e.printStackTrace(); } finally { if (fr != null ){ try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } } } @Test public void testFileReader1 () { FileReader fr = null ; try { File file = new File ("hello.txt" ); fr = new FileReader (file); char [] cbuf = new char [5 ]; int len; while ((len = fr.read(cbuf)) != -1 ){ String str = new String (cbuf,0 ,len); System.out.print(str); } } catch (IOException e) { e.printStackTrace(); } finally { if (fr != null ){ try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } } } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
三、节点流(或文件流)
四、缓冲流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 @Test public void BufferedStreamTest () throws FileNotFoundException { BufferedInputStream bis = null ; BufferedOutputStream bos = null ; try { File srcFile = new File ("爱情与友情.jpg" ); File destFile = new File ("爱情与友情3.jpg" ); FileInputStream fis = new FileInputStream ((srcFile)); FileOutputStream fos = new FileOutputStream (destFile); bis = new BufferedInputStream (fis); bos = new BufferedOutputStream (fos); byte [] buffer = new byte [10 ]; int len; while ((len = bis.read(buffer)) != -1 ){ bos.write(buffer,0 ,len); } } catch (IOException e) { e.printStackTrace(); } finally { if (bos != null ){ try { bos.close(); } catch (IOException e) { e.printStackTrace(); } } if (bis != null ){ try { bis.close(); } catch (IOException e) { e.printStackTrace(); } } } } public void copyFileWithBuffered (String srcPath,String destPath) { BufferedInputStream bis = null ; BufferedOutputStream bos = null ; try { File srcFile = new File (srcPath); File destFile = new File (destPath); FileInputStream fis = new FileInputStream ((srcFile)); FileOutputStream fos = new FileOutputStream (destFile); bis = new BufferedInputStream (fis); bos = new BufferedOutputStream (fos); byte [] buffer = new byte [1024 ]; int len; while ((len = bis.read(buffer)) != -1 ){ bos.write(buffer,0 ,len); } } catch (IOException e) { e.printStackTrace(); } finally { if (bos != null ){ try { bos.close(); } catch (IOException e) { e.printStackTrace(); } } if (bis != null ){ try { bis.close(); } catch (IOException e) { e.printStackTrace(); } } } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
五、转换流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Test public void test2 () throws Exception { File file1 = new File ("dbcp.txt" ); File file2 = new File ("dbcp_gbk.txt" ); FileInputStream fis = new FileInputStream (file1); FileOutputStream fos = new FileOutputStream (file2); InputStreamReader isr = new InputStreamReader (fis,"utf-8" ); OutputStreamWriter osw = new OutputStreamWriter (fos,"gbk" ); char [] cbuf = new char [20 ]; int len; while ((len = isr.read(cbuf)) != -1 ){ osw.write(cbuf,0 ,len); } isr.close(); osw.close(); } 12345678910111213141516171819202122232425
详情见:计算机字符编码
六、标准输入、输出流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public static void main (String[] args) { BufferedReader br = null ; try { InputStreamReader isr = new InputStreamReader (System.in); br = new BufferedReader (isr); while (true ) { System.out.println("请输入字符串:" ); String data = br.readLine(); if ("e" .equalsIgnoreCase(data) || "exit" .equalsIgnoreCase(data)) { System.out.println("程序结束" ); break ; } String upperCase = data.toUpperCase(); System.out.println(upperCase); } } catch (IOException e) { e.printStackTrace(); } finally { if (br != null ) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } } 12345678910111213141516171819202122232425262728293031
七、打印流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 @Test public void test2 () { PrintStream ps = null ; try { FileOutputStream fos = new FileOutputStream (new File ("D:\\IO\\text.txt" )); ps = new PrintStream (fos, true ); if (ps != null ) { System.setOut(ps); } for (int i = 0 ; i <= 255 ; i++) { System.out.print((char ) i); if (i % 50 == 0 ) { System.out.println(); } } } catch (FileNotFoundException e) { e.printStackTrace(); } finally { if (ps != null ) { ps.close(); } } } 1234567891011121314151617181920212223242526272829
八、数据流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Test public void test3 () throws IOException { DataOutputStream dos = new DataOutputStream (new FileOutputStream ("data.txt" )); dos.writeUTF("刘建辰" ); dos.flush(); dos.writeInt(23 ); dos.flush(); dos.writeBoolean(true ); dos.flush(); dos.close(); } 12345678910111213141516
九、对象流
十、随机存取文件流
十一、NIO.2中Path、Paths、Files类的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 public class PathTest { @Test public void test1 () { Path path1 = Paths.get("d:\\nio\\hello.txt" ); Path path2 = Paths.get("d:\\" , "nio\\hello.txt" ); System.out.println(path1); System.out.println(path2); Path path3 = Paths.get("d:\\" , "nio" ); System.out.println(path3); } @Test public void test2 () { Path path1 = Paths.get("d:\\" , "nio\\nio1\\nio2\\hello.txt" ); Path path2 = Paths.get("hello.txt" ); System.out.println(path1); System.out.println(path1.startsWith("d:\\nio" )); System.out.println(path1.endsWith("hello.txt" )); System.out.println(path1.isAbsolute() + "~" ); System.out.println(path2.isAbsolute() + "~" ); System.out.println(path1.getParent()); System.out.println(path2.getParent()); System.out.println(path1.getRoot()); System.out.println(path2.getRoot()); System.out.println(path1.getFileName() + "~" ); System.out.println(path2.getFileName() + "~" ); for (int i = 0 ; i < path1.getNameCount(); i++) { System.out.println(path1.getName(i) + "*****" ); } System.out.println(path1.toAbsolutePath()); System.out.println(path2.toAbsolutePath()); Path path3 = Paths.get("d:\\" , "nio" ); Path path4 = Paths.get("nioo\\hi.txt" ); path3 = path3.resolve(path4); System.out.println(path3); File file = path1.toFile(); Path newPath = file.toPath(); } }
网络编程 一、网络编程概述
二、网络通信要素概述
三、通信要素1:IP和端口号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public static void main (String[] args) { try { InetAddress inet1 = InetAddress.getByName("192.168.10.14" ); System.out.println(inet1); InetAddress inet2 = InetAddress.getByName("www.baidu.com" ); System.out.println(inet2); InetAddress inet3 = InetAddress.getByName("127.0.0.1" ); System.out.println(inet3); InetAddress inet4 = InetAddress.getLocalHost(); System.out.println(inet4); System.out.println(inet2.getHostName()); System.out.println(inet2.getHostAddress()); } catch (UnknownHostException e) { e.printStackTrace(); } } 123456789101112131415161718192021222324252627
五、TCP网络编程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 @Test public void client () throws IOException { Socket socket = new Socket (InetAddress.getByName("127.0.0.1" ),9090 ); OutputStream os = socket.getOutputStream(); FileInputStream fis = new FileInputStream (new File ("beauty.jpg" )); byte [] buffer = new byte [1024 ]; int len; while ((len = fis.read(buffer)) != -1 ){ os.write(buffer,0 ,len); } socket.shutdownOutput(); InputStream is = socket.getInputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream (); byte [] bufferr = new byte [20 ]; int len1; while ((len1 = is.read(buffer)) != -1 ){ baos.write(buffer,0 ,len1); } System.out.println(baos.toString()); fis.close(); os.close(); socket.close(); baos.close(); } @Test public void server () throws IOException { ServerSocket ss = new ServerSocket (9090 ); Socket socket = ss.accept(); InputStream is = socket.getInputStream(); FileOutputStream fos = new FileOutputStream (new File ("beauty2.jpg" )); byte [] buffer = new byte [1024 ]; int len; while ((len = is.read(buffer)) != -1 ){ fos.write(buffer,0 ,len); } System.out.println("图片传输完成" ); OutputStream os = socket.getOutputStream(); os.write("你好,美女,照片我已收到,非常漂亮!" .getBytes()); fos.close(); is.close(); socket.close(); ss.close(); os.close(); } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
六、 UDP网络编程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 @Test public void sender () throws IOException { DatagramSocket socket = new DatagramSocket (); String str = "我是UDP方式发送的导弹" ; byte [] data = str.getBytes(); InetAddress inet = InetAddress.getLocalHost(); DatagramPacket packet = new DatagramPacket (data,0 ,data.length,inet,9090 ); socket.send(packet); socket.close(); } @Test public void receiver () throws IOException { DatagramSocket socket = new DatagramSocket (9090 ); byte [] buffer = new byte [100 ]; DatagramPacket packet = new DatagramPacket (buffer,0 ,buffer.length); socket.receive(packet); System.out.println(new String (packet.getData(),0 ,packet.getLength())); socket.close(); } 123456789101112131415161718192021222324252627282930313233
七、URL编程
1 2 3 4 5 6 7 8 URL url = new URL ("http://localhost:8080/examples/myTest.txt" );System.out.println("getProtocol() :" +url.getProtocol()); System.out.println("getHost() :" +url.getHost()); System.out.println("getPort() :" +url.getPort()); System.out.println("getPath() :" +url.getPath()); System.out.println("getFile() :" +url.getFile()); System.out.println("getQuery() :" +url.getQuery()); 1234567
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public static void main (String[] args) { HttpURLConnection urlConnection = null ; InputStream is = null ; FileOutputStream fos = null ; try { URL url = new URL ("http://localhost:8080/examples/beauty.jpg" ); urlConnection = (HttpURLConnection) url.openConnection(); urlConnection.connect(); is = urlConnection.getInputStream(); fos = new FileOutputStream ("beauty3.jpg" ); byte [] buffer = new byte [1024 ]; int len; while ((len = is.read(buffer)) != -1 ){ fos.write(buffer,0 ,len); } System.out.println("下载完成" ); } catch (IOException e) { e.printStackTrace(); } finally { if (is != null ){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if (fos != null ){ try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if (urlConnection != null ){ urlConnection.disconnect(); } } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445
反射 一、Java反射机制概述
二、理解Class类并获取Class的实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Class clazz1 = Person.class; System.out.println(clazz1); Person p1 = new Person (); Class clazz2 = p1.getClass(); System.out.println(clazz2); Class clazz3 = Class.forName("com.java.Person" ); System.out.println(clazz3); System.out.println(clazz1 == clazz2); System.out.println(clazz1 == clazz3); ClassLoader classLoader = ReflectionTest.class.getClassLoader(); Class clazz4 = classLoader.loadClass("com.atguigu.java.Person" ); System.out.println(clazz4); System.out.println(clazz1 == clazz4); 12345678910111213141516171819202122
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Class c1 = Object.class;Class c2 = Comparable.class;Class c3 = String[].class;Class c4 = int [][].class;Class c5 = ElementType.class;Class c6 = Override.class;Class c7 = int .class;Class c8 = void .class;Class c9 = Class.class;int [] a = new int [10 ];int [] b = new int [100 ];Class c10 = a.getClass();Class c11 = b.getClass();System.out.println(c10 == c11); 123456789101112131415
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class ClassLoadingTest { public static void main (String[] args) { System.out.println(A.m); } } class A { static { m = 300 ; } static int m = 100 ; } 123456789101112131415161718
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 @Test public void test1 () { ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); System.out.println(classLoader); ClassLoader classLoader1 = classLoader.getParent(); System.out.println(classLoader1); ClassLoader classLoader2 = classLoader1.getParent(); System.out.println(classLoader2); ClassLoader classLoader3 = String.class.getClassLoader(); System.out.println(classLoader3); } 1234567891011121314151617 ClassLoader classloader = ClassLoader.getSystemClassLoader(); System.out.println(classloader); classloader = classloader.getParent(); System.out.println(classloader); classloader = classloader.getParent(); System.out.println(classloader); classloader = Class.forName("exer2.ClassloaderDemo" ).getClassLoader(); System.out.println(classloader); classloader = Class.forName("java.lang.Object" ).getClassLoader(); System.out.println(classloader); InputStream in = null ; in = this .getClass().getClassLoader().getResourceAsStream("exer2\\test.properties" ); System.out.println(in); 1234567891011121314151617181920 @Test public void test2 () throws Exception { Properties pros = new Properties (); ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); InputStream is = classLoader.getResourceAsStream("jdbc1.properties" ); pros.load(is); String user = pros.getProperty("user" ); String password = pros.getProperty("password" ); System.out.println("user = " + user + ",password = " + password); } 123456789101112131415161718192021222324
四、创建运行时类的对象
1 2 3 4 5 6 7 8 9 10 String name = “atguigu.java.Person"; Class clazz = null; clazz = Class.forName(name); //2.调用指定参数结构的构造器,生成Constructor的实例 Constructor con = clazz.getConstructor(String.class,Integer.class); //3.通过Constructor的实例创建对应类的对象,并初始化类属性 Person p2 = (Person) con.newInstance(" Peter",20); System.out.println(p2); 123456789
五、获取运行时类的完整结构
获取运行时类的完整结构示例
六、调用运行时类的指定结构 调用运行时类指定结构示例
七、反射的应用:动态代理 动态代理详解
java8新特性
一、JDK8中新日期时间API JDK8中新日期时间API
java中的Lambda表达式
java8中的StreamAPI
四、Optional类 两个测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 public class Girl { private String name; @Override public String toString () { return "Girl{" + "name='" + name + '\'' + '}' ; } public String getName () { return name; } public void setName (String name) { this .name = name; } public Girl () { } public Girl (String name) { this .name = name; } } 1234567891011121314151617181920212223242526272829 public class Boy { private Girl girl; @Override public String toString () { return "Boy{" + "girl=" + girl + '}' ; } public Girl getGirl () { return girl; } public void setGirl (Girl girl) { this .girl = girl; } public Boy () { } public Boy (Girl girl) { this .girl = girl; } } 12345678910111213141516171819202122232425262728 public class OptionalTest { @Test public void test1 () { Girl girl = new Girl (); Optional<Girl> optionalGirl = Optional.of(girl); } @Test public void test2 () { Girl girl = new Girl (); Optional<Girl> optionalGirl = Optional.ofNullable(girl); System.out.println(optionalGirl); Girl girl1 = optionalGirl.orElse(new Girl ("赵丽颖" )); System.out.println(girl1); } public String getGirlName (Boy boy) { return boy.getGirl().getName(); } @Test public void test3 () { Boy boy = new Boy (); boy = null ; String girlName = getGirlName(boy); System.out.println(girlName); } public String getGirlName1 (Boy boy) { if (boy != null ){ Girl girl = boy.getGirl(); if (girl != null ){ return girl.getName(); } } return null ; } @Test public void test4 () { Boy boy = new Boy (); boy = null ; String girlName = getGirlName1(boy); System.out.println(girlName); } public String getGirlName2 (Boy boy) { Optional<Boy> boyOptional = Optional.ofNullable(boy); Boy boy1 = boyOptional.orElse(new Boy (new Girl ("迪丽热巴" ))); Girl girl = boy1.getGirl(); Optional<Girl> girlOptional = Optional.ofNullable(girl); Girl girl1 = girlOptional.orElse(new Girl ("古力娜扎" )); return girl1.getName(); } @Test public void test5 () { Boy boy = null ; boy = new Boy (); boy = new Boy (new Girl ("苍老师" )); String girlName = getGirlName2(boy); System.out.println(girlName); } }
java中jdbc 第1章:JDBC概述 1.1 数据的持久化
持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用 。大多数情况下,特别是企业级应用,数据持久化意味着将内存中的数据保存到硬盘 上加以”固化”,而持久化的实现过程大多通过各种关系数据库来完成 。
持久化的主要应用是将内存中的数据存储在关系型数据库中,当然也可以存储在磁盘文件、XML数据文件中。
1.2 Java中的数据存储技术
在Java中,数据库存取技术可分为如下几类:
JDBC 直接访问数据库
JDO (Java Data Object )技术
第三方O/R工具 ,如Hibernate, Mybatis 等
JDBC是java访问数据库的基石,JDO、Hibernate、MyBatis等只是更好的封装了JDBC。
1.3 JDBC介绍
JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口 (一组API),定义了用来访问数据库的标准Java类库,(java.sql,javax.sql )使用这些类库可以以一种标准 的方法、方便地访问数据库资源。
JDBC为访问不同的数据库提供了一种统一的途径 ,为开发者屏蔽了一些细节问题。
JDBC的目标是使Java程序员使用JDBC可以连接任何提供了JDBC驱动程序 的数据库系统,这样就使得程序员无需对特定的数据库系统的特点有过多的了解,从而大大简化和加快了开发过程。
如果没有JDBC,那么Java程序访问数据库时是这样的:
1.4 JDBC体系结构
JDBC接口(API)包括两个层次:
面向应用的API :Java API,抽象接口,供应用程序开发人员使用(连接数据库,执行SQL语句,获得结果)。
面向数据库的API :Java Driver API,供开发商开发数据库驱动程序用。
JDBC是sun公司提供一套用于数据库操作的接口,java程序员只需要面向这套接口编程即可。
不同的数据库厂商,需要针对这套接口,提供不同实现。不同的实现的集合,即为不同数据库的驱动。 ————面向接口编程
1.5 JDBC程序编写步骤
补充:ODBC(Open Database Connectivity ,开放式数据库连接),是微软在Windows平台下推出的。使用者在程序中只需要调用ODBC API,由 ODBC 驱动程序将调用转换成为对特定的数据库的调用请求。
第2章:获取数据库连接 2.1 要素一:Driver接口实现类 2.1.1 Driver接口介绍
java.sql.Driver 接口是所有 JDBC 驱动程序需要实现的接口。这个接口是提供给数据库厂商使用的,不同数据库厂商提供不同的实现。
在程序中不需要直接去访问实现了 Driver 接口的类,而是由驱动程序管理器类(java.sql.DriverManager)去调用这些Driver实现。
Oracle的驱动:oracle.jdbc.driver.OracleDriver
mySql的驱动: com.mysql.jdbc.Driver 将上述jar包拷贝到Java工程的一个目录中,习惯上新建一个lib文件夹。 注意:如果是Dynamic Web Project(动态的web项目)话,则是把驱动jar放到WebContent(有的开发工具叫WebRoot)目录中的WEB-INF目录中的lib目录下即可
2.1.2 加载与注册JDBC驱动
加载驱动:加载 JDBC 驱动需调用 Class 类的静态方法 forName(),向其传递要加载的 JDBC 驱动的类名
Class.forName(“com.mysql.jdbc.Driver”);
注册驱动:DriverManager 类是驱动程序管理器类,负责管理驱动程序
使用DriverManager.registerDriver(com.mysql.jdbc.Driver)来注册驱动
通常不用显式调用 DriverManager 类的 registerDriver() 方法来注册驱动程序类的实例,因为 Driver 接口的驱动程序类都 包含了静态代码块,在这个静态代码块中,会调用 DriverManager.registerDriver() 方法来注册自身的一个实例。下图是MySQL的Driver实现类的源码:
2.2 要素二:URL
JDBC URL 用于标识一个被注册的驱动程序,驱动程序管理器通过这个 URL 选择正确的驱动程序,从而建立到数据库的连接。
JDBC URL的标准由三部分组成,各部分间用冒号分隔。
jdbc:子协议:子名称
协议 :JDBC URL中的协议总是jdbc
子协议 :子协议用于标识一个数据库驱动程序
子名称 :一种标识数据库的方法。子名称可以依不同的子协议而变化,用子名称的目的是为了定位数据库 提供足够的信息。包含主机名 (对应服务端的ip地址),端口号,数据库名
举例:
几种常用数据库的 JDBC URL
MySQL的连接URL编写方式:
jdbc:mysql://主机名称:mysql服务端口号/数据库名称?参数=值&参数=值
jdbc:mysql://localhost:3306/test
jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8(如果JDBC程序与服务器端的字符集不一致,会导致乱码,那么可以通过参数指定服务器端的字符集)
jdbc:mysql://localhost:3306/test?user=root&password=123456
Oracle 9i的连接URL编写方式:
jdbc:oracle:thin:@主机名称:oracle服务端口号:数据库名称
jdbc:oracle:thin:@localhost:1521:test
SQLServer的连接URL编写方式:
jdbc:sqlserver://主机名称:sqlserver服务端口号:DatabaseName=数据库名称
jdbc:sqlserver://localhost:1433:DatabaseName=test
2.3 要素三:用户名和密码
user,password可以用“属性名=属性值”方式告诉数据库
可以调用 DriverManager 类的 getConnection() 方法建立到数据库的连接
2.4 数据库连接方式举例 2.4.1 连接方式一 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Test public void testConnection1 () { try { Driver driver = null ; driver = new com .mysql.jdbc.Driver(); String url = "jdbc:mysql://localhost:3306/test" ; Properties info = new Properties (); info.setProperty("user" , "root" ); info.setProperty("password" , "abc123" ); Connection conn = driver.connect(url, info); System.out.println(conn); } catch (SQLException e) { e.printStackTrace(); } } 12345678910111213141516171819202122
说明:上述代码中显式出现了第三方数据库的API
2.4.2 连接方式二 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @Test public void testConnection2 () { try { String className = "com.mysql.jdbc.Driver" ; Class clazz = Class.forName(className); Driver driver = (Driver) clazz.newInstance(); String url = "jdbc:mysql://localhost:3306/test" ; Properties info = new Properties (); info.setProperty("user" , "root" ); info.setProperty("password" , "abc123" ); Connection conn = driver.connect(url, info); System.out.println(conn); } catch (Exception e) { e.printStackTrace(); } } 123456789101112131415161718192021222324
说明:相较于方式一,这里使用反射实例化Driver,不在代码中体现第三方数据库的API。体现了面向接口编程思想。
2.4.3 连接方式三 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Test public void testConnection3 () { try { String url = "jdbc:mysql://localhost:3306/test" ; String user = "root" ; String password = "abc123" ; String driverName = "com.mysql.jdbc.Driver" ; Class clazz = Class.forName(driverName); Driver driver = (Driver) clazz.newInstance(); DriverManager.registerDriver(driver); Connection conn = DriverManager.getConnection(url, user, password); System.out.println(conn); } catch (Exception e) { e.printStackTrace(); } } 12345678910111213141516171819202122
说明:使用DriverManager实现数据库的连接。体会获取连接必要的4个基本要素。
2.4.4 连接方式四 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 @Test public void testConnection4 () { try { String url = "jdbc:mysql://localhost:3306/test" ; String user = "root" ; String password = "abc123" ; String driverName = "com.mysql.jdbc.Driver" ; Class.forName(driverName); Connection conn = DriverManager.getConnection(url, user, password); System.out.println(conn); } catch (Exception e) { e.printStackTrace(); } } 12345678910111213141516171819202122232425262728293031323334353637
说明:不必显式的注册驱动了。因为在DriverManager的源码中已经存在静态代码块,实现了驱动的注册。
2.4.5 连接方式五(最终版) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Test public void testConnection5 () throws Exception { InputStream is = ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties" ); Properties pros = new Properties (); pros.load(is); String user = pros.getProperty("user" ); String password = pros.getProperty("password" ); String url = pros.getProperty("url" ); String driverClass = pros.getProperty("driverClass" ); Class.forName(driverClass); Connection conn = DriverManager.getConnection(url,user,password); System.out.println(conn); } 123456789101112131415161718192021
其中,配置文件声明在工程的src目录下:【jdbc.properties】
1 2 3 4 5 user =root password =abc123 url =jdbc:mysql://localhost:3306/test driverClass =com.mysql.jdbc.Driver 1234
说明:使用配置文件的方式保存配置信息,在代码中加载配置文件
使用配置文件的好处:
①实现了代码和数据的分离,如果需要修改配置信息,直接在配置文件中修改,不需要深入代码 ②如果修改了配置信息,省去重新编译的过程。
第3章:使用PreparedStatement实现CRUD操作 3.1 操作和访问数据库
数据库连接被用于向数据库服务器发送命令和 SQL 语句,并接受数据库服务器返回的结果。其实一个数据库连接就是一个Socket连接。
在 java.sql 包中有 3 个接口分别定义了对数据库的调用的不同方式:
Statement:用于执行静态 SQL 语句并返回它所生成结果的对象。
PrepatedStatement:SQL 语句被预编译并存储在此对象中,可以使用此对象多次高效地执行该语句。
CallableStatement:用于执行 SQL 存储过程
3.2 使用Statement操作数据表的弊端
通过调用 Connection 对象的 createStatement() 方法创建该对象。该对象用于执行静态的 SQL 语句,并且返回执行结果。
Statement 接口中定义了下列方法用于执行 SQL 语句:
1 2 3 int excuteUpdate(String sql ):执行更新操作INSERT 、UPDATE 、DELETE ResultSet executeQuery(String sql ):执行查询操作SELECT 12
但是使用Statement操作数据表存在弊端:
问题一:存在拼串操作,繁琐
问题二:存在SQL注入问题
SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句段或命令(如:SELECT user, password FROM user_table WHERE user=‘a’ OR 1 = ’ AND password = ’ OR ‘1’ = ‘1’) ,从而利用系统的 SQL 引擎完成恶意行为的做法。
对于 Java 而言,要防范 SQL 注入,只要用 PreparedStatement(从Statement扩展而来) 取代 Statement 就可以了。
代码演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 public class StatementTest { @Test public void testLogin () { Scanner scan = new Scanner (System.in); System.out.print("用户名:" ); String userName = scan.nextLine(); System.out.print("密 码:" ); String password = scan.nextLine(); String sql = "SELECT user,password FROM user_table WHERE USER = '" + userName + "' AND PASSWORD = '" + password + "'" ; User user = get(sql, User.class); if (user != null ) { System.out.println("登陆成功!" ); } else { System.out.println("用户名或密码错误!" ); } } public <T> T get (String sql, Class<T> clazz) { T t = null ; Connection conn = null ; Statement st = null ; ResultSet rs = null ; try { InputStream is = StatementTest.class.getClassLoader().getResourceAsStream("jdbc.properties" ); Properties pros = new Properties (); pros.load(is); String user = pros.getProperty("user" ); String password = pros.getProperty("password" ); String url = pros.getProperty("url" ); String driverClass = pros.getProperty("driverClass" ); Class.forName(driverClass); conn = DriverManager.getConnection(url, user, password); st = conn.createStatement(); rs = st.executeQuery(sql); ResultSetMetaData rsmd = rs.getMetaData(); int columnCount = rsmd.getColumnCount(); if (rs.next()) { t = clazz.newInstance(); for (int i = 0 ; i < columnCount; i++) { String columnName = rsmd.getColumnLabel(i + 1 ); Object columnVal = rs.getObject(columnName); Field field = clazz.getDeclaredField(columnName); field.setAccessible(true ); field.set(t, columnVal); } return t; } } catch (Exception e) { e.printStackTrace(); } finally { if (rs != null ) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (st != null ) { try { st.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null ) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } return null ; } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
综上:
3.3 PreparedStatement的使用 3.3.1 PreparedStatement介绍
可以通过调用 Connection 对象的 preparedStatement(String sql) 方法获取 PreparedStatement 对象
PreparedStatement 接口是 Statement 的子接口,它表示一条预编译过的 SQL 语句
PreparedStatement 对象所代表的 SQL 语句中的参数用问号(?)来表示,调用 PreparedStatement 对象的 setXxx() 方法来设置这些参数. setXxx() 方法有两个参数,第一个参数是要设置的 SQL 语句中的参数的索引(从 1 开始),第二个是设置的 SQL 语句中的参数的值
3.3.2 PreparedStatement vs Statement
代码的可读性和可维护性。
PreparedStatement 能最大可能提高性能:
DBServer会对预编译 语句提供性能优化。因为预编译语句有可能被重复调用,所以语句在被DBServer的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中就会得到执行。
在statement语句中,即使是相同操作但因为数据内容不一样,所以整个语句本身不能匹配,没有缓存语句的意义.事实是没有数据库会对普通语句编译后的执行代码缓存。这样每执行一次都要对传入的语句编译一次。
(语法检查,语义检查,翻译成二进制命令,缓存)
PreparedStatement 可以防止 SQL 注入
3.3.3 Java与SQL对应数据类型转换表
Java类型
SQL类型
boolean
BIT
byte
TINYINT
short
SMALLINT
int
INTEGER
long
BIGINT
String
CHAR,VARCHAR,LONGVARCHAR
byte array
BINARY , VAR BINARY
java.sql.Date
DATE
java.sql.Time
TIME
java.sql.Timestamp
TIMESTAMP
3.3.4 使用PreparedStatement实现增、删、改操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public void update (String sql,Object ... args) { Connection conn = null ; PreparedStatement ps = null ; try { conn = JDBCUtils.getConnection(); ps = conn.prepareStatement(sql); for (int i = 0 ;i < args.length;i++){ ps.setObject(i + 1 , args[i]); } ps.execute(); } catch (Exception e) { e.printStackTrace(); }finally { JDBCUtils.closeResource(conn, ps); } } 1234567891011121314151617181920212223242526
3.3.5 使用PreparedStatement实现查询操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 public <T> T getInstance (Class<T> clazz, String sql, Object... args) { Connection conn = null ; PreparedStatement ps = null ; ResultSet rs = null ; try { conn = JDBCUtils.getConnection(); ps = conn.prepareStatement(sql); for (int i = 0 ; i < args.length; i++) { ps.setObject(i + 1 , args[i]); } rs = ps.executeQuery(); ResultSetMetaData rsmd = rs.getMetaData(); int columnCount = rsmd.getColumnCount(); if (rs.next()) { T t = clazz.newInstance(); for (int i = 0 ; i < columnCount; i++) { Object columnVal = rs.getObject(i + 1 ); String columnLabel = rsmd.getColumnLabel(i + 1 ); Field field = clazz.getDeclaredField(columnLabel); field.setAccessible(true ); field.set(t, columnVal); } return t; } } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.closeResource(conn, ps, rs); } return null ; } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
说明:使用PreparedStatement实现的查询操作可以替换Statement实现的查询操作,解决Statement拼串和SQL注入问题。
3.4.1 ResultSet
查询需要调用PreparedStatement 的 executeQuery() 方法,查询结果是一个ResultSet 对象
ResultSet 对象以逻辑表格的形式封装了执行数据库操作的结果集,ResultSet 接口由数据库厂商提供实现
ResultSet 返回的实际上就是一张数据表。有一个指针指向数据表的第一条记录的前面。
ResultSet 对象维护了一个指向当前数据行的游标 ,初始的时候,游标在第一行之前,可以通过 ResultSet 对象的 next() 方法移动到下一行。调用 next()方法检测下一行是否有效。若有效,该方法返回 true,且指针下移。相当于Iterator对象的 hasNext() 和 next() 方法的结合体。
当指针指向一行时, 可以通过调用 getXxx(int index) 或 getXxx(int columnName) 获取每一列的值。
例如: getInt(1), getString(“name”)
注意:Java与数据库交互涉及到的相关Java API中的索引都从1开始。
ResultSet 接口的常用方法:
boolean next()
getString()
…
可用于获取关于 ResultSet 对象中列的类型和属性信息的对象
ResultSetMetaData meta = rs.getMetaData();
getColumnName (int column):获取指定列的名称
getColumnLabel (int column):获取指定列的别名
getColumnCount ():返回当前 ResultSet 对象中的列数。
getColumnTypeName(int column):检索指定列的数据库特定的类型名称。
getColumnDisplaySize(int column):指示指定列的最大标准宽度,以字符为单位。
isNullable (int column):指示指定列中的值是否可以为 null。
isAutoIncrement(int column):指示是否自动为指定列进行编号,这样这些列仍然是只读的。
问题1:得到结果集后, 如何知道该结果集中有哪些列 ? 列名是什么?
需要使用一个描述 ResultSet 的对象, 即 ResultSetMetaData
问题2:关于ResultSetMetaData
如何获取 ResultSetMetaData : 调用 ResultSet 的 getMetaData() 方法即可
获取 ResultSet 中有多少列 :调用 ResultSetMetaData 的 getColumnCount() 方法
获取 ResultSet 每一列的列的别名是什么 :调用 ResultSetMetaData 的getColumnLabel() 方法
3.5 资源的释放
释放ResultSet, Statement,Connection。
数据库连接(Connection)是非常稀有的资源,用完后必须马上释放,如果Connection不能及时正确的关闭将导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放。
可以在finally中关闭,保证及时其他代码出现异常,资源也一定能被关闭。
3.6 JDBC API小结
两种思想
面向接口编程的思想
ORM思想(object relational mapping)
一个数据表对应一个java类
表中的一条记录对应java类的一个对象
表中的一个字段对应java类的一个属性
sql是需要结合列名和表的属性名来写。注意起别名。
两种技术
JDBC结果集的元数据:ResultSetMetaData
获取列数:getColumnCount()
获取列的别名:getColumnLabel()
通过反射,创建指定类的对象,获取指定的属性并赋值
第4章 操作BLOB类型字段 4.1 MySQL BLOB类型
MySQL中,BLOB是一个二进制大型对象,是一个可以存储大量数据的容器,它能容纳不同大小的数据。
插入BLOB类型的数据必须使用PreparedStatement,因为BLOB类型的数据无法使用字符串拼接写的。
MySQL的四种BLOB类型(除了在存储的最大信息量上不同外,他们是等同的)
实际使用中根据需要存入的数据大小定义不同的BLOB类型。
需要注意的是:如果存储的文件过大,数据库的性能会下降。
如果在指定了相关的Blob类型以后,还报错:xxx too large,那么在mysql的安装目录下,找my.ini文件加上如下的配置参数: max_allowed_packet=16M 。同时注意:修改了my.ini文件之后,需要重新启动mysql服务。
4.2 向数据表中插入大数据 类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Connection conn = JDBCUtils.getConnection(); String sql = "insert into customers(name,email,birth,photo)values(?,?,?,?)" ;PreparedStatement ps = conn.prepareStatement(sql);ps.setString(1 , "徐海强" ); ps.setString(2 , "xhq@126.com" ); ps.setDate(3 , new Date (new java .util.Date().getTime())); FileInputStream fis = new FileInputStream ("xhq.png" );ps.setBlob(4 , fis); ps.execute(); fis.close(); JDBCUtils.closeResource(conn, ps); 12345678910111213141516171819
4.3 修改数据表中的Blob类型字段 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Connection conn = JDBCUtils.getConnection();String sql = "update customers set photo = ? where id = ?" ;PreparedStatement ps = conn.prepareStatement(sql);FileInputStream fis = new FileInputStream ("coffee.png" );ps.setBlob(1 , fis); ps.setInt(2 , 25 ); ps.execute(); fis.close(); JDBCUtils.closeResource(conn, ps); 1234567891011121314
4.4 从数据表中读取大数据类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 String sql = "SELECT id, name, email, birth, photo FROM customer WHERE id = ?" ;conn = getConnection(); ps = conn.prepareStatement(sql); ps.setInt(1 , 8 ); rs = ps.executeQuery(); if (rs.next()){ Integer id = rs.getInt(1 ); String name = rs.getString(2 ); String email = rs.getString(3 ); Date birth = rs.getDate(4 ); Customer cust = new Customer (id, name, email, birth); System.out.println(cust); Blob photo = rs.getBlob(5 ); InputStream is = photo.getBinaryStream(); OutputStream os = new FileOutputStream ("c.jpg" ); byte [] buffer = new byte [1024 ]; int len = 0 ; while ((len = is.read(buffer)) != -1 ){ os.write(buffer, 0 , len); } JDBCUtils.closeResource(conn, ps, rs); if (is != null ){ is.close(); } if (os != null ){ os.close(); } }
第5章 批量插入 当需要成批插入或者更新记录时,可以采用Java的批量更新 机制,这一机制允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率
JDBC的批量处理语句包括下面三个方法:
addBatch(String):添加需要批量处理的SQL语句或是参数;
executeBatch():执行批量处理语句;
clearBatch():清空缓存的数据
通常我们会遇到两种批量执行SQL语句的情况:
多条SQL语句的批量处理;
一个SQL语句的批量传参;
5.2 高效的批量插入 举例:向数据表中插入20000条数据
1 2 3 4 5 CREATE TABLE goods(id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR (20 ) ); 1234
5.2.1 实现层次一:使用Statement 1 2 3 4 5 6 7 Connection conn = JDBCUtils.getConnection();Statement st = conn.createStatement();for (int i = 1 ;i <= 20000 ;i++){ String sql = "insert into goods(name) values('name_' + " + i +")" ; st.executeUpdate(sql); } 123456
5.2.2 实现层次二:使用PreparedStatement 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 long start = System.currentTimeMillis(); Connection conn = JDBCUtils.getConnection(); String sql = "insert into goods(name)values(?)" ;PreparedStatement ps = conn.prepareStatement(sql);for (int i = 1 ;i <= 20000 ;i++){ ps.setString(1 , "name_" + i); ps.executeUpdate(); } long end = System.currentTimeMillis();System.out.println("花费的时间为:" + (end - start)); JDBCUtils.closeResource(conn, ps); 12345678910111213141516
5.2.3 实现层次三 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 @Test public void testInsert1 () throws Exception{ long start = System.currentTimeMillis(); Connection conn = JDBCUtils.getConnection(); String sql = "insert into goods(name)values(?)" ; PreparedStatement ps = conn.prepareStatement(sql); for (int i = 1 ;i <= 1000000 ;i++){ ps.setString(1 , "name_" + i); ps.addBatch(); if (i % 500 == 0 ){ ps.executeBatch(); ps.clearBatch(); } } long end = System.currentTimeMillis(); System.out.println("花费的时间为:" + (end - start)); JDBCUtils.closeResource(conn, ps); } 12345678910111213141516171819202122232425262728293031323334
5.2.4 实现层次四 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 @Test public void testInsert2 () throws Exception{ long start = System.currentTimeMillis(); Connection conn = JDBCUtils.getConnection(); conn.setAutoCommit(false ); String sql = "insert into goods(name)values(?)" ; PreparedStatement ps = conn.prepareStatement(sql); for (int i = 1 ;i <= 1000000 ;i++){ ps.setString(1 , "name_" + i); ps.addBatch(); if (i % 500 == 0 ){ ps.executeBatch(); ps.clearBatch(); } } conn.commit(); long end = System.currentTimeMillis(); System.out.println("花费的时间为:" + (end - start)); JDBCUtils.closeResource(conn, ps); }
第6章: 数据库事务 6.1 数据库事务介绍
事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态。
**事务处理(事务操作):**保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都**被提交(commit),那么这些修改就永久地保存下来;要么数据库管理系统将放弃所作的所有修改,整个事务 回滚(rollback)**到最初状态。
为确保数据库中数据的一致性 ,数据的操纵应当是离散的成组的逻辑单元:当它全部完成时,数据的一致性可以保持,而当这个单元中的一部分操作失败,整个事务应全部视为错误,所有从起始点以后的操作应全部回退到开始状态。
6.2 JDBC事务处理
【案例:用户AA向用户BB转账100】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public void testJDBCTransaction () { Connection conn = null ; try { conn = JDBCUtils.getConnection(); conn.setAutoCommit(false ); String sql1 = "update user_table set balance = balance - 100 where user = ?" ; update(conn, sql1, "AA" ); String sql2 = "update user_table set balance = balance + 100 where user = ?" ; update(conn, sql2, "BB" ); conn.commit(); } catch (Exception e) { e.printStackTrace(); try { conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } } finally { try { conn.setAutoCommit(true ); } catch (SQLException e) { e.printStackTrace(); } JDBCUtils.closeResource(conn, null , null ); } } 1234567891011121314151617181920212223242526272829303132333435363738
其中,对数据库操作的方法为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public void update (Connection conn ,String sql, Object... args) { PreparedStatement ps = null ; try { ps = conn.prepareStatement(sql); for (int i = 0 ; i < args.length; i++) { ps.setObject(i + 1 , args[i]); } ps.execute(); } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.closeResource(null , ps); } } 1234567891011121314151617181920
6.3 事务的ACID属性
原子性(Atomicity) 原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性(Consistency) 事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
隔离性(Isolation) 事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性(Durability) 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。
6.3.1 数据库的并发 问题
对于同时运行的多个事务, 当这些事务访问数据库中相同的数据时, 如果没有采取必要的隔离机制, 就会导致各种并发问题:
脏读 : 对于两个事务 T1, T2, T1 读取了已经被 T2 更新但还没有被提交 的字段。之后, 若 T2 回滚, T1读取的内容就是临时且无效的。
不可重复读 : 对于两个事务T1, T2, T1 读取了一个字段, 然后 T2 更新 了该字段。之后, T1再次读取同一个字段, 值就不同了。
幻读 : 对于两个事务T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入 了一些新的行。之后, 如果 T1 再次读取同一个表, 就会多出几行。
数据库事务的隔离性 : 数据库系统必须具有隔离并发运行各个事务的能力, 使它们不会相互影响, 避免各种并发问题。
一个事务与其他事务隔离的程度称为隔离级别。数据库规定了多种事务隔离级别, 不同隔离级别对应不同的干扰程度, 隔离级别越高, 数据一致性就越好, 但并发性越弱。
6.3.2 四种隔离级别
数据库提供的4种事务隔离级别:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6m0gPvK8-1592151802471)(尚硅谷_宋红康_JDBC.assets/1555586275271.png)]
Oracle 支持的 2 种事务隔离级别:READ COMMITED , SERIALIZABLE。 Oracle 默认的事务隔离级别为: READ COMMITED 。
Mysql 支持 4 种事务隔离级别。Mysql 默认的事务隔离级别为: REPEATABLE READ。
6.3.3 在MySql中设置隔离级别
第7章:DAO及相关实现类
DAO:Data Access Object访问数据信息的类和接口,包括了对数据的CRUD(Create、Retrival、Update、Delete),而不包含任何业务相关的信息。有时也称作:BaseDAO
作用:为了实现功能的模块化,更有利于代码的维护和升级。
【BaseDAO.java】 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 package com.atguigu.bookstore.dao;import java.lang.reflect.ParameterizedType;import java.lang.reflect.Type;import java.sql.Connection;import java.sql.SQLException;import java.util.List;import org.apache.commons.dbutils.QueryRunner;import org.apache.commons.dbutils.handlers.BeanHandler;import org.apache.commons.dbutils.handlers.BeanListHandler;import org.apache.commons.dbutils.handlers.ScalarHandler;public abstract class BaseDao <T> { private QueryRunner queryRunner = new QueryRunner (); private Class<T> type; public BaseDao () { Class clazz = this .getClass(); ParameterizedType parameterizedType = (ParameterizedType) clazz.getGenericSuperclass(); Type[] types = parameterizedType.getActualTypeArguments(); this .type = (Class<T>) types[0 ]; } public int update (Connection conn,String sql, Object... params) { int count = 0 ; try { count = queryRunner.update(conn, sql, params); } catch (SQLException e) { e.printStackTrace(); } return count; } public T getBean (Connection conn,String sql, Object... params) { T t = null ; try { t = queryRunner.query(conn, sql, new BeanHandler <T>(type), params); } catch (SQLException e) { e.printStackTrace(); } return t; } public List<T> getBeanList (Connection conn,String sql, Object... params) { List<T> list = null ; try { list = queryRunner.query(conn, sql, new BeanListHandler <T>(type), params); } catch (SQLException e) { e.printStackTrace(); } return list; } public Object getValue (Connection conn,String sql, Object... params) { Object count = null ; try { count = queryRunner.query(conn, sql, new ScalarHandler <>(), params); } catch (SQLException e) { e.printStackTrace(); } return count; } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
【BookDAO.java】 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 package com.atguigu.bookstore.dao;import java.sql.Connection;import java.util.List;import com.atguigu.bookstore.beans.Book;import com.atguigu.bookstore.beans.Page;public interface BookDao { List<Book> getBooks (Connection conn) ; void saveBook (Connection conn,Book book) ; void deleteBookById (Connection conn,String bookId) ; Book getBookById (Connection conn,String bookId) ; void updateBook (Connection conn,Book book) ; Page<Book> getPageBooks (Connection conn,Page<Book> page) ; Page<Book> getPageBooksByPrice (Connection conn,Page<Book> page, double minPrice, double maxPrice) ; } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
【UserDAO.java】 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.atguigu.bookstore.dao;import java.sql.Connection;import com.atguigu.bookstore.beans.User;public interface UserDao { User getUser (Connection conn,User user) ; boolean checkUsername (Connection conn,User user) ; void saveUser (Connection conn,User user) ; } 12345678910111213141516171819202122232425262728293031
【BookDaoImpl.java】 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 package com.atguigu.bookstore.dao.impl;import java.sql.Connection;import java.util.List;import com.atguigu.bookstore.beans.Book;import com.atguigu.bookstore.beans.Page;import com.atguigu.bookstore.dao.BaseDao;import com.atguigu.bookstore.dao.BookDao;public class BookDaoImpl extends BaseDao <Book> implements BookDao { @Override public List<Book> getBooks (Connection conn) { List<Book> beanList = null ; String sql = "select id,title,author,price,sales,stock,img_path imgPath from books" ; beanList = getBeanList(conn,sql); return beanList; } @Override public void saveBook (Connection conn,Book book) { String sql = "insert into books(title,author,price,sales,stock,img_path) values(?,?,?,?,?,?)" ; update(conn,sql, book.getTitle(), book.getAuthor(), book.getPrice(), book.getSales(), book.getStock(),book.getImgPath()); } @Override public void deleteBookById (Connection conn,String bookId) { String sql = "DELETE FROM books WHERE id = ?" ; update(conn,sql, bookId); } @Override public Book getBookById (Connection conn,String bookId) { Book book = null ; String sql = "select id,title,author,price,sales,stock,img_path imgPath from books where id = ?" ; book = getBean(conn,sql, bookId); return book; } @Override public void updateBook (Connection conn,Book book) { String sql = "update books set title = ? , author = ? , price = ? , sales = ? , stock = ? where id = ?" ; update(conn,sql, book.getTitle(), book.getAuthor(), book.getPrice(), book.getSales(), book.getStock(), book.getId()); } @Override public Page<Book> getPageBooks (Connection conn,Page<Book> page) { String sql = "select count(*) from books" ; long totalRecord = (long ) getValue(conn,sql); page.setTotalRecord((int ) totalRecord); String sql2 = "select id,title,author,price,sales,stock,img_path imgPath from books limit ?,?" ; List<Book> beanList = getBeanList(conn,sql2, (page.getPageNo() - 1 ) * Page.PAGE_SIZE, Page.PAGE_SIZE); page.setList(beanList); return page; } @Override public Page<Book> getPageBooksByPrice (Connection conn,Page<Book> page, double minPrice, double maxPrice) { String sql = "select count(*) from books where price between ? and ?" ; long totalRecord = (long ) getValue(conn,sql,minPrice,maxPrice); page.setTotalRecord((int ) totalRecord); String sql2 = "select id,title,author,price,sales,stock,img_path imgPath from books where price between ? and ? limit ?,?" ; List<Book> beanList = getBeanList(conn,sql2, minPrice , maxPrice , (page.getPageNo() - 1 ) * Page.PAGE_SIZE, Page.PAGE_SIZE); page.setList(beanList); return page; } } 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
【UserDaoImpl.java】 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package com.atguigu.bookstore.dao.impl;import java.sql.Connection;import com.atguigu.bookstore.beans.User;import com.atguigu.bookstore.dao.BaseDao;import com.atguigu.bookstore.dao.UserDao;public class UserDaoImpl extends BaseDao <User> implements UserDao { @Override public User getUser (Connection conn,User user) { User bean = null ; String sql = "select id,username,password,email from users where username = ? and password = ?" ; bean = getBean(conn,sql, user.getUsername(), user.getPassword()); return bean; } @Override public boolean checkUsername (Connection conn,User user) { User bean = null ; String sql = "select id,username,password,email from users where username = ?" ; bean = getBean(conn,sql, user.getUsername()); return bean != null ; } @Override public void saveUser (Connection conn,User user) { String sql = "insert into users(username,password,email) values(?,?,?)" ; update(conn,sql, user.getUsername(),user.getPassword(),user.getEmail()); } } 123456789101112131415161718192021222324252627282930313233343536373839
【Book.java】 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.atguigu.bookstore.beans;public class Book { private Integer id; private String title; private String author; private double price; private Integer sales; private Integer stock; private String imgPath = "static/img/default.jpg" ; } 12345678910111213141516
【Page.java】 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.atguigu.bookstore.beans;import java.util.List;public class Page <T> { private List<T> list; public static final int PAGE_SIZE = 4 ; private int pageNo; private int totalRecord; 123456789101112131415
【User.java】 1 2 3 4 5 6 7 8 9 10 11 package com.atguigu.bookstore.beans;public class User { private Integer id; private String username; private String password; private String email;
第8章:数据库连接池 8.1 JDBC数据库连接池的必要性
在使用开发基于数据库的web程序时,传统的模式基本是按以下步骤:
在主程序(如servlet、beans)中建立数据库连接
进行sql操作
断开数据库连接
这种模式开发,存在的问题:
普通的JDBC数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将 Connection 加载到内存中,再验证用户名和密码(得花费0.05s~1s的时间)。需要数据库连接的时候,就向数据库要求一个,执行完成后再断开连接。这样的方式将会消耗大量的资源和时间。数据库的连接资源并没有得到很好的重复利用 若同时有几百人甚至几千人在线,频繁的进行数据库连接操作将占用很多的系统资源,严重的甚至会造成服务器的崩溃。
对于每一次数据库连接,使用完后都得断开 否则,如果程序出现异常而未能关闭,将会导致数据库系统中的内存泄漏,最终将导致重启数据库。(回忆:何为Java的内存泄漏?)
这种开发不能控制被创建的连接对象数 ,系统资源会被毫无顾及的分配出去,如连接过多,也可能导致内存泄漏,服务器崩溃。
8.2 数据库连接池技术
为解决传统开发中的数据库连接问题,可以采用数据库连接池技术。
数据库连接池的基本思想 :就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。
数据库连接池 负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个 。
数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数来设定 的。无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量 限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。
数据库连接池技术的优点
1. 资源重用
由于数据库连接得以重用,避免了频繁创建,释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增加了系统运行环境的平稳性。
2. 更快的系统反应速度
数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于连接池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而减少了系统的响应时间
3. 新的资源分配手段
对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接池的配置,实现某一应用最大可用数据库连接数的限制,避免某一应用独占所有的数据库资源
4. 统一的连接管理,避免数据库连接泄漏
在较为完善的数据库连接池实现中,可根据预先的占用超时设定,强制回收被占用连接,从而避免了常规数据库连接操作中可能出现的资源泄露
8.3 多种开源的数据库连接池
JDBC 的数据库连接池使用 javax.sql.DataSource 来表示,DataSource 只是一个接口,该接口通常由服务器(Weblogic, WebSphere, Tomcat)提供实现,也有一些开源组织提供实现:
DBCP 是Apache提供的数据库连接池。tomcat 服务器自带dbcp数据库连接池。速度相对c3p0较快 ,但因自身存在BUG,Hibernate3已不再提供支持。
C3P0 是一个开源组织提供的一个数据库连接池,速度相对较慢,稳定性还可以。 hibernate官方推荐使用
Proxool 是sourceforge下的一个开源项目数据库连接池,有监控连接池状态的功能,稳定性较c3p0差一点
BoneCP 是一个开源组织提供的数据库连接池,速度快
Druid 是阿里提供的数据库连接池,据说是集DBCP 、C3P0 、Proxool 优点于一身的数据库连接池,但是速度不确定是否有BoneCP快
DataSource 通常被称为数据源,它包含连接池和连接池管理两个部分,习惯上也经常把 DataSource 称为连接池
DataSource用来取代DriverManager来获取Connection,获取速度快,同时可以大幅度提高数据库访问速度。
特别注意:
数据源和数据库连接不同,数据源无需创建多个,它是产生数据库连接的工厂,因此整个应用只需要一个数据源即可。
当数据库访问结束后,程序还是像以前一样关闭数据库连接:conn.close(); 但conn.close()并没有关闭数据库的物理连接,它仅仅把数据库连接释放,归还给了数据库连接池。
8.3.1 C3P0数据库连接池
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static Connection getConnection1 () throws Exception{ ComboPooledDataSource cpds = new ComboPooledDataSource (); cpds.setDriverClass("com.mysql.jdbc.Driver" ); cpds.setJdbcUrl("jdbc:mysql://localhost:3306/test" ); cpds.setUser("root" ); cpds.setPassword("abc123" ); Connection conn = cpds.getConnection(); return conn; } 12345678910111213
1 2 3 4 5 6 7 private static DataSource cpds = new ComboPooledDataSource ("helloc3p0" );public static Connection getConnection2 () throws SQLException{ Connection conn = cpds.getConnection(); return conn; } 123456
其中,src下的配置文件为:【c3p0-config.xml】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <?xml version="1.0" encoding="UTF-8" ?> <c3p0-config > <named-config name ="helloc3p0" > <property name ="user" > root</property > <property name ="password" > abc123</property > <property name ="jdbcUrl" > jdbc:mysql:///test</property > <property name ="driverClass" > com.mysql.jdbc.Driver</property > <property name ="acquireIncrement" > 5</property > <property name ="initialPoolSize" > 5</property > <property name ="minPoolSize" > 5</property > <property name ="maxPoolSize" > 10</property > <property name ="maxStatements" > 20</property > <property name ="maxStatementsPerConnection" > 5</property > </named-config > </c3p0-config > 12345678910111213141516171819202122232425
8.3.2 DBCP数据库连接池
DBCP 是 Apache 软件基金组织下的开源连接池实现,该连接池依赖该组织下的另一个开源系统:Common-pool。如需使用该连接池实现,应在系统中增加如下两个 jar 文件:
Commons-dbcp.jar:连接池的实现
Commons-pool.jar:连接池实现的依赖库
Tomcat 的连接池正是采用该连接池来实现的 该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。
数据源和数据库连接不同,数据源无需创建多个,它是产生数据库连接的工厂,因此整个应用只需要一个数据源即可。
当数据库访问结束后,程序还是像以前一样关闭数据库连接:conn.close(); 但上面的代码并没有关闭数据库的物理连接,它仅仅把数据库连接释放,归还给了数据库连接池。
配置属性说明
属性
默认值
说明
initialSize
0
连接池启动时创建的初始化连接数量
maxActive
8
连接池中可同时连接的最大的连接数
maxIdle
8
连接池中最大的空闲的连接数,超过的空闲连接将被释放,如果设置为负数表示不限制
minIdle
0
连接池中最小的空闲的连接数,低于这个数量会被创建新的连接。该参数越接近maxIdle,性能越好,因为连接的创建和销毁,都是需要消耗资源的;但是不能太大。
maxWait
无限制
最大等待时间,当没有可用连接时,连接池等待连接释放的最大时间,超过该时间限制会抛出异常,如果设置-1表示无限等待
poolPreparedStatements
false
开启池的Statement是否prepared
maxOpenPreparedStatements
无限制
开启池的prepared 后的同时最大连接数
minEvictableIdleTimeMillis
连接池中连接,在时间段内一直空闲, 被逐出连接池的时间
removeAbandonedTimeout
300
超过时间限制,回收没有用(废弃)的连接
removeAbandoned
false
超过removeAbandonedTimeout时间后,是否进 行没用连接(废弃)的回收
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public static Connection getConnection3 () throws Exception { BasicDataSource source = new BasicDataSource (); source.setDriverClassName("com.mysql.jdbc.Driver" ); source.setUrl("jdbc:mysql:///test" ); source.setUsername("root" ); source.setPassword("abc123" ); source.setInitialSize(10 ); Connection conn = source.getConnection(); return conn; } 1234567891011121314
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 private static DataSource source = null ;static { try { Properties pros = new Properties (); InputStream is = DBCPTest.class.getClassLoader().getResourceAsStream("dbcp.properties" ); pros.load(is); source = BasicDataSourceFactory.createDataSource(pros); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection4 () throws Exception { Connection conn = source.getConnection(); return conn; } 12345678910111213141516171819202122
其中,src下的配置文件为:【dbcp.properties】
1 2 3 4 5 6 7 8 driverClassName =com.mysql.jdbc.Driver url =jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true&useServerPrepStmts=false username =root password =abc123 initialSize =10 1234567
8.3.3 Druid (德鲁伊)数据库连接池 Druid是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0、DBCP、Proxool等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB连接池,可以说是目前最好的连接池之一。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.atguigu.druid;import java.sql.Connection;import java.util.Properties;import javax.sql.DataSource;import com.alibaba.druid.pool.DruidDataSourceFactory;public class TestDruid { public static void main (String[] args) throws Exception { Properties pro = new Properties (); pro.load(TestDruid.class.getClassLoader().getResourceAsStream("druid.properties" )); DataSource ds = DruidDataSourceFactory.createDataSource(pro); Connection conn = ds.getConnection(); System.out.println(conn); } } 123456789101112131415161718
其中,src下的配置文件为:【druid.properties】
1 2 3 4 5 6 7 8 9 10 url=jdbc:mysql: username=root password=123456 driverClassName=com.mysql.jdbc.Driver initialSize=10 maxActive=20 maxWait=1000 filters=wall 123456789
配置
缺省
说明
name
配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。 如果没有配置,将会生成一个名字,格式是:”DataSource-” + System.identityHashCode(this)
url
连接数据库的url,不同数据库不一样。例如:mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username
连接数据库的用户名
password
连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里:https://github.com/alibaba/druid/wiki/使用ConfigFilter
driverClassName
根据url自动识别 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName(建议配置下)
initialSize
0
初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
maxActive
8
最大连接池数量
maxIdle
8
已经不再使用,配置了也没效果
minIdle
最小连接池数量
maxWait
获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
poolPreparedStatements
false
是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
maxOpenPreparedStatements
-1
要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
validationQuery
用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。
testOnBorrow
true
申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testOnReturn
false
归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
testWhileIdle
false
建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
timeBetweenEvictionRunsMillis
有两个含义: 1)Destroy线程会检测连接的间隔时间2)testWhileIdle的判断依据,详细看testWhileIdle属性的说明
numTestsPerEvictionRun
不再使用,一个DruidDataSource只支持一个EvictionRun
minEvictableIdleTimeMillis
connectionInitSqls
物理连接初始化的时候执行的sql
exceptionSorter
根据dbType自动识别 当数据库抛出一些不可恢复的异常时,抛弃连接
filters
属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
proxyFilters
类型是List,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系
第9章:Apache-DBUtils实现CRUD操作 9.1 Apache-DBUtils简介
commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。
API介绍:
org.apache.commons.dbutils.QueryRunner
org.apache.commons.dbutils.ResultSetHandler
工具类:org.apache.commons.dbutils.DbUtils
API包说明:
9.2 主要API的使用 9.2.1 DbUtils
DbUtils :提供如关闭连接、装载JDBC驱动程序等常规工作的工具类,里面的所有方法都是静态的。主要方法如下:
public static void close(…) throws java.sql.SQLException : DbUtils类提供了三个重载的关闭方法。这些方法检查所提供的参数是不是NULL,如果不是的话,它们就关闭Connection、Statement和ResultSet。
public static void closeQuietly(…): 这一类方法不仅能在Connection、Statement和ResultSet为NULL情况下避免关闭,还能隐藏一些在程序中抛出的SQLEeception。
public static void commitAndClose(Connection conn)throws SQLException: 用来提交连接的事务,然后关闭连接
public static void commitAndCloseQuietly(Connection conn): 用来提交连接,然后关闭连接,并且在关闭连接时不抛出SQL异常。
public static void rollback(Connection conn)throws SQLException:允许conn为null,因为方法内部做了判断
public static void rollbackAndClose(Connection conn)throws SQLException
rollbackAndCloseQuietly(Connection)
public static boolean loadDriver(java.lang.String driverClassName):这一方装载并注册JDBC驱动程序,如果成功就返回true。使用该方法,你不需要捕捉这个异常ClassNotFoundException。
9.2.2 QueryRunner类
该类简单化了SQL查询,它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量。
QueryRunner类提供了两个构造器:
默认的构造器
需要一个 javax.sql.DataSource 来作参数的构造器
QueryRunner类的主要方法:
更新
public int update(Connection conn, String sql, Object… params) throws SQLException:用来执行一个更新(插入、更新或删除)操作。
…
插入
public T insert(Connection conn,String sql,ResultSetHandler rsh, Object… params) throws SQLException:只支持INSERT语句,其中 rsh - The handler used to create the result object from the ResultSet of auto-generated keys. 返回值: An object generated by the handler.即自动生成的键值
…
批处理
public int[] batch(Connection conn,String sql,Object[][] params)throws SQLException: INSERT, UPDATE, or DELETE语句
public T insertBatch(Connection conn,String sql,ResultSetHandler rsh,Object[][] params)throws SQLException:只支持INSERT语句
…
查询
public Object query(Connection conn, String sql, ResultSetHandler rsh,Object… params) throws SQLException:执行一个查询操作,在这个查询中,对象数组中的每个元素值被用来作为查询语句的置换参数。该方法会自行处理 PreparedStatement 和 ResultSet 的创建和关闭。
…
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @Test public void testInsert () throws Exception { QueryRunner runner = new QueryRunner (); Connection conn = JDBCUtils.getConnection3(); String sql = "insert into customers(name,email,birth)values(?,?,?)" ; int count = runner.update(conn, sql, "何成飞" , "he@qq.com" , "1992-09-08" ); System.out.println("添加了" + count + "条记录" ); JDBCUtils.closeResource(conn, null ); } 12345678910111213 @Test public void testDelete () throws Exception { QueryRunner runner = new QueryRunner (); Connection conn = JDBCUtils.getConnection3(); String sql = "delete from customers where id < ?" ; int count = runner.update(conn, sql,3 ); System.out.println("删除了" + count + "条记录" ); JDBCUtils.closeResource(conn, null ); } 12345678910111213
9.2.3 ResultSetHandler接口及实现类
该接口用于处理 java.sql.ResultSet,将数据按要求转换为另一种形式。
ResultSetHandler 接口提供了一个单独的方法:Object handle (java.sql.ResultSet .rs)。
接口的主要实现类:
ArrayHandler:把结果集中的第一行数据转成对象数组。
ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。
BeanHandler: 将结果集中的第一行数据封装到一个对应的JavaBean实例中。
BeanListHandler: 将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
ColumnListHandler:将结果集中某一列的数据存放到List中。
KeyedHandler(name):将结果集中的每一行数据都封装到一个Map里,再把这些map再存到一个map里,其key为指定的key。
MapHandler: 将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
MapListHandler: 将结果集中的每一行数据都封装到一个Map里,然后再存放到List
ScalarHandler: 查询单个值对象
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 @Test public void testQueryInstance () throws Exception{ QueryRunner runner = new QueryRunner (); Connection conn = JDBCUtils.getConnection3(); String sql = "select id,name,email,birth from customers where id = ?" ; BeanHandler<Customer> handler = new BeanHandler <>(Customer.class); Customer customer = runner.query(conn, sql, handler, 23 ); System.out.println(customer); JDBCUtils.closeResource(conn, null ); } 12345678910111213141516171819 @Test public void testQueryList () throws Exception{ QueryRunner runner = new QueryRunner (); Connection conn = JDBCUtils.getConnection3(); String sql = "select id,name,email,birth from customers where id < ?" ; BeanListHandler<Customer> handler = new BeanListHandler <>(Customer.class); List<Customer> list = runner.query(conn, sql, handler, 23 ); list.forEach(System.out::println); JDBCUtils.closeResource(conn, null ); } 1234567891011121314151617181920 @Test public void testQueryInstance1 () throws Exception{ QueryRunner runner = new QueryRunner (); Connection conn = JDBCUtils.getConnection3(); String sql = "select id,name,email,birth from customers where id = ?" ; ResultSetHandler<Customer> handler = new ResultSetHandler <Customer>() { @Override public Customer handle (ResultSet rs) throws SQLException { System.out.println("handle" ); if (rs.next()){ int id = rs.getInt("id" ); String name = rs.getString("name" ); String email = rs.getString("email" ); Date birth = rs.getDate("birth" ); return new Customer (id, name, email, birth); } return null ; } }; Customer customer = runner.query(conn, sql, handler, 23 ); System.out.println(customer); JDBCUtils.closeResource(conn, null ); } 12345678910111213141516171819202122232425262728293031323334353637 @Test public void testQueryValue () throws Exception{ QueryRunner runner = new QueryRunner (); Connection conn = JDBCUtils.getConnection3(); String sql = "select max(birth) from customers" ; ScalarHandler handler = new ScalarHandler (); Date birth = (Date) runner.query(conn, sql, handler); System.out.println(birth); JDBCUtils.closeResource(conn, null ); } 12345678910111213141516171819202122232425
JDBC总结 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 总结 @Test public void testUpdateWithTx () { Connection conn = null ; try { conn.commit(); } catch (Exception e) { e.printStackTrace(); try { conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } }finally { } }
comparator 使用外部比较器 Comparator 进行排序 当我们需要对集合的元素进行排序的时候,可以使用 java.util.Comparator 创建一个比较器来进行排序。Comparator 接口同样也是一个函数式接口,我们可以把使用 lambda 表达式。如下示例,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 package com.common;import java.util.*;import java.util.stream.Collectors;public class ComparatorTest { public static void main (String[] args) { Employee e1 = new Employee ("John" , 25 , 3000 , 9922001 ); Employee e2 = new Employee ("Ace" , 22 , 2000 , 5924001 ); Employee e3 = new Employee ("Keith" , 35 , 4000 , 3924401 ); List<Employee> employees = new ArrayList <>(); employees.add(e1); employees.add(e2); employees.add(e3); employees.sort((o1, o2) -> o1.getName().compareTo(o2.getName())); Collections.sort(employees, (o1, o2) -> o1.getName().compareTo(o2.getName())); employees.forEach(System.out::println); } } class Employee { String name; int age; double salary; long mobile; public Employee (String name, int age, double salary, long mobile) { this .name = name; this .age = age; this .salary = salary; this .mobile = mobile; } public String getName () { return name; } public void setName (String name) { this .name = name; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } public double getSalary () { return salary; } public void setSalary (double salary) { this .salary = salary; } public long getMobile () { return mobile; } public void setMobile (long mobile) { this .mobile = mobile; } @Override public String toString () { final StringBuilder sb = new StringBuilder ("Employee{" ); sb.append("name='" ).append(name).append('\'' ); sb.append(", age=" ).append(age); sb.append(", salary=" ).append(salary); sb.append(", mobile=" ).append(mobile); sb.append('}' ); return sb.toString(); } }
使用 Comparator.comparing 进行排序 comparing 方法一 查看 Comparator 类内部实现,还有一个 comparing 方法,实现如下,
1 2 3 4 5 6 7 public static <T, U extends Comparable <? super U>> Comparator<T> comparing ( Function<? super T, ? extends U> keyExtractor) { Objects.requireNonNull(keyExtractor); return (Comparator<T> & Serializable) (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2)); }
其返回值是 (c1, c2) -> keyExtractor.apply (c1).compareTo (keyExtractor.apply (c2)); 一个 lambda 表达式,也就是一个 Compator 。所以上面那个例子也可以改造成 如下,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 package com.common;import java.util.*;public class ComparatorTest { public static void main (String[] args) { Employee e1 = new Employee ("John" , 25 , 3000 , 9922001 ); Employee e2 = new Employee ("Ace" , 22 , 2000 , 5924001 ); Employee e3 = new Employee ("Keith" , 35 , 4000 , 3924401 ); List<Employee> employees = new ArrayList <>(); employees.add(e1); employees.add(e2); employees.add(e3); employees.sort((o1, o2) -> o1.getName().compareTo(o2.getName())); Collections.sort(employees, (o1, o2) -> o1.getName().compareTo(o2.getName())); employees.forEach(System.out::println); employees.sort(Comparator.comparing(e -> e.getName())); employees.sort(Comparator.comparing(Employee::getName)); } } class Employee { String name; int age; double salary; long mobile; public Employee (String name, int age, double salary, long mobile) { this .name = name; this .age = age; this .salary = salary; this .mobile = mobile; } public String getName () { return name; } public void setName (String name) { this .name = name; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } public double getSalary () { return salary; } public void setSalary (double salary) { this .salary = salary; } public long getMobile () { return mobile; } public void setMobile (long mobile) { this .mobile = mobile; } @Override public String toString () { final StringBuilder sb = new StringBuilder ("Employee{" ); sb.append("name='" ).append(name).append('\'' ); sb.append(", age=" ).append(age); sb.append(", salary=" ).append(salary); sb.append(", mobile=" ).append(mobile); sb.append('}' ); return sb.toString(); } }
comparing 方法二 1 2 3 4 5 6 7 8 9 10 public static <T, U> Comparator<T> comparing ( Function<? super T, ? extends U> keyExtractor, Comparator<? super U> keyComparator) { Objects.requireNonNull(keyExtractor); Objects.requireNonNull(keyComparator); return (Comparator<T> & Serializable) (c1, c2) -> keyComparator.compare(keyExtractor.apply(c1), keyExtractor.apply(c2)); }
和 comparing 方法一不同的是 该方法多了一个参数 keyComparator ,keyComparator 是创建一个自定义的比较器。
1 2 3 4 Collections.sort(employees, Comparator.comparing( Employee::getName, (s1, s2) -> { return s2.compareTo(s1); }));
使用 *Comparator.reversed* 进行排序 返回相反的排序规则,
1 2 3 4 5 6 Collections.sort(employees, Comparator.comparing(Employee::getName).reversed()); employees.forEach(System.out::println);
输出,
1 2 3 Employee{name='Keith', age=35, salary=4000.0, mobile=3924401} Employee{name='John', age=25, salary=3000.0, mobile=9922001} Employee{name='Ace', age=22, salary=2000.0, mobile=5924001}
使用 ***Comparator.***nullsFirst 进行排序 当集合中存在 null 元素时,可以使用针对 null 友好的比较器,null 元素排在集合的最前面
1 2 3 4 5 6 7 employees.add(null ); Collections.sort(employees, Comparator.nullsFirst(Comparator.comparing(Employee::getName))); employees.forEach(System.out::println); Collections.sort(employees, Comparator.nullsLast(Comparator.comparing(Employee::getName))); employees.forEach(System.out::println);
使用 Comparator*.*thenComparing 排序 首先使用 name 排序,紧接着在使用 ege 排序,来看下使用效果
1 2 Collections.sort(employees, Comparator.comparing(Employee::getAge).thenComparing(Employee::getName)); employees.forEach(System.out::println);
collections.copy 拷贝一个ArrayList对象到另一个ArrayList对象中,正好发现Collections有一个copy方法。可是不好用啊总是报错。查看api才知道,它的capacity(容纳能力大小)可以指定(最好指定)。而初始化时size的大小永远默认为0,只有在进行add和remove等相关操作 时,size的大小才变化。然而进行copy()时候,首先做的是将desc的size和src的size大小进行比较,只有当desc的 size 大于或者等于src的size时才进行拷贝,否则抛出IndexOutOfBoundsException异常;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 import java.util.ArrayList;import java.util.Arrays;import java.util.Collections;import java.util.List; public class test1 { public static void main (String[] args) { List<String> src = new ArrayList <String>(); src.add("111" ); src.add("222" ); src.add("333" ); src.add("444" ); List<String> dic = new ArrayList <String>(Arrays.asList(new String [src .size()])); Collections.copy(dic, src); for (Object s : dic) { System.out.println(s); } } }
第二种办法: dest1 = new ArrayList(); Collections.addAll(dest1, new Object[src1.size()]); Collections.copy(dest1, src1);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public static void main (String[] args) { List<String> src = new ArrayList <String>(); src.add("111" ); src.add("222" ); src.add("333" ); src.add("444" ); List<String> dest1 = new ArrayList <String>(); Collections.addAll(dest1, new String [src.size()]); Collections.copy(dest1, src); for (Object s : dest1) { System.out.println(s); }