一、为什么选择Java(Java的优势)
1、应用面广:
相较于其他语言,Java的应用面可谓是非常广,这得益于他的跨平台性和其性能的稳定性。他在服务器后端,Android应用开发,大数据开发,研发开发工具方面有着其他语言无可匹敌的岗位数量。
2、性能优越,生态丰富:
Java的虚拟机JVM有着优秀的性能优化和垃圾回收机制,并且Java具有强大的多线程支持,能够有效的处理并发和并行计算,提高系统的吞吐量。
Java有着庞大的开发者社区和活跃的开源社区供开发人员学习,有助于加快开发进度。并且Java有着丰富的类包,让编程变得更加简单。
3、学习周期短,性价比更高:
相较于c/c++,Java的学习周期更短,学习内容更加简单,易于入门。与python相比,虽然相对学习周期长,但由于Java优秀的性能、并发处理和安全性更受企业的青睐,所以岗位和工资相对比python更高,也就更具有性价比。
4、就业岗位多
二、Java发展史
在1991年时候,James Gosling(詹姆斯•高斯林)所在sun公司的工程师小组,需要 设计一种小型计算机语言,该语言主要用于像电视机顶盒,家用电器等这样的消费类电子 产品,这些电子产品有一个共同的特点:计算处理能力和内存都非常有限,所以要求:语 言必须能够生成非常紧凑的代码,这样才能在这样的环境中执行,另外,由于不同的厂商 选择不同的CPU,因此,要求该语言不能和特定的体系结构绑在一起,要求语言本身是 中立的,也就是在不同的平台上运行(即跨平台)。 所以在sun公司内部成立了一个Green项目组,该小组的领导人是James Gosling。 他们经过4年的研发,最终于1995年正式确立.将名字改为java,Java是印度尼西亚爪哇岛 的英文名称,因盛产咖啡而闻名。JAVA的标识也正是一杯正冒着热气的咖啡
1996年 java1.0版本正式发布
java开始版本号 是 1.1 1.2 1.3 1.4
从java1.5开始版本号改为 java5 java6 jav7 java8 .....java21
现在每半年更新一次 每年 3月和9月
三、Java语言的特点
·开源:开放源代码,自己就可以修改,免费使用。
·简单:摒弃了指针这一概念,操作更加方便快捷。
·平台无关性:得益于JVM,Java程序只需要开发一次,就可以在不同操作系统上运行
·面向对象编程:提高代码的模块化、可扩展性、可维护性和可读性,同时提供了代码复用和抽象的机制,使得开发人员能够更好地管理和处理复杂的系统。
·支持网络:使得Java有了网络编程库这一强大功能。
·多线程机制:提高并发性、提高性能、改善用户体验、支持异步编程、实现并发设计模式、提高资源利用率,使得Java成为一种非常适合开发并发和多任务处理的语言。
·动态内存管理机制 自动垃圾回收 java中你只需申请内存空间, 何时释放空间,你不需要关心
·安全:Java的安全性得益于其语言级的安全特性、内置的安全机制、安全管理器、安全的类库、安全的网络编程支持、沙箱机制和垃圾回收机制等,使得Java成为一种非常安全的编程语言
·生态丰富:,Java生态系统的丰富性得益于其广泛的应用领域、成熟的开发工具和框架、大量的开源库和组件、丰富的第三方工具和服务、丰富的社区资源和支持、跨平台特性等
·高性能:Java在高性能方面具有先进的即时编译技术、多线程支持、高效的网络编程支持、垃圾回收机制等特点,并且拥有丰富的优化工具和框架以及广泛的优化技术和最佳实践
三、Java的技术体系平台
JavaSE:Java Standard Edition(标准版): java语言的基础 包含java语言中最核心的类(库)
JavaEE:Java Enterprise Edition(企业版): servlet = server(服务) + applet(应用程序) 服务器端的程序
企业版中包含标准版,还包含企业级开发相关的类
JavaME:Java Micro Edition(微缩版) : 早期电子产品开发, 现在已被安卓开发取代
安卓开发: 主要开发安卓手机app 只要掌握javaSE知识,就可以学习安卓开发
四、Java语言运行机制
首先编写一个.java文件(源代码,程序员认识但是机器不认识),通过java环境编译为class文件(字节码文件,Java指令规范),最后将.class文件交给java虚拟机运行,编译为机器码,由CPU执行。
五、Java环境搭建(JDK工具包的安装)
1、下载JDK,链接https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html
2、安装:建议寻找专门位置安装,将开发工具统一存储。
3、配置jdk/bin目录到 系统环境变量path中
首先右键此电脑点击属性
点击高级系统设置
点击环境变量
在系统变量栏中双击Path
点击新建,讲你下载的JDK/bin的位置复制进去,创建完成后上移至第一位。
4、测试安装是否成功:在任意目录中使用Java命令
在目录中新建创建Hello.txt文件(记事本),输入下列代码并保存
public class Hello{
public static void main(String[] args){
System.out.print("hello world");
}
}
更改后缀为.java文件
在目录处输入cmd
输入javac Hello.java
java Hello测试功能
六、IDE的安装
网址IntelliJ IDEA – 领先的 Java 和 Kotlin IDE
选择下载版本并安装
安装完成后点击新建项目,创建文件夹存放工程
进入后左键点击scr->新建->java类即可创建
推荐在校生在官网申请免费一年的专业版账号
七、Java数据类型-String
String属于引用数据类型,是Java类库中提供的一个类,用类来当做字符串变量类型
声明方式,例:
String str="我是谁";
与基本类型的运算规则:任意八种基本类型的数据与String类型数据进行”+“运算时,结果仍为字符串,String不能转换为其他基本类型。
八、基本数据类型之间的转换
8种基本数据类型除了布尔型以外,其他7种之间可以进行相互转换
默认转换
转换规则:将取值范围小的类型可以自动转换成取值范围大的类型
char ---》
byte ---》 int ---》 long ---》 float ---》 double
short ---》
注:4字节的float类型表数范围大于8字节的long,浮点数在存储时结构与整数计算方式不同。
强制转换
容量大的数据类型转换为容量小的数据类型时,要加上强制转换符,但可能造 成精 度降低或溢出;使用时要格外注意,例:
int a = 258;
byte b = (byte)a;
此时b的值为2,原因:int占4个字节,00000000|00000000|00000001|00000010
byte占1个字节,则前面三个字节数据溢出,只剩下了00000010
在多种数据进行混合运算时,会自动将所有数据转换为表数范围最大的类型再进行计算。
九、运算符
1.算数运算
运算符 运算 案例 结果
+ 正号 +3 3
- 负号 b=4;-b -4
+ 加 5+5 10
- 减 514-114 400
* 乘 3*4 12
/ 除 5/3 1
% 取模 5%3 2
++ 自增:在前先运算后取值,在后先取值后运算 a=2;b=++a;c=a++; a=3 b=3 c=2
-- 自减:在前先运算后取值,在后先取值后运算 a=2;b=--a;c=a--; a=1 b=1 c=2
+ 字符串相加 "He"+"llo" "Hello"
2.比较运算符
运算符 运算 案例 结果
== 相等于 4==3 false
!= 不等于 4!=3 true
< 小于 4<3 false
> 大于 4>3 true
<= 不大于 4<=3 false
>= 不小于 4>=3 true
instanceof 检查是否是类的对象 "hello" instanceof String true
比较运算符的结果都为boolean型,也就是要么true,要么是false。
3.逻辑运算符
&——逻辑与 |——逻辑或 !——逻辑非
&&——短路与 ||——短路或 ^——逻辑异或
a b a&b a|b !a a^b a&&b a||b
true true true true false false true true
true false false true false true false true
false true false true true true false true
false false false false true false false false
”&“和”&&“的区别:
单个&时,左边无论真假,右边表达式都进行运算;
两个&时,若左边为真,右边参与运算;当左边为假,右边不参加运算;
”|“和”||“同理。
4.赋值运算符
符号:=
扩展赋值运算符:+=、-=、*=、/=、%=
例:s += 2——>s = s+2,其余同理
经典案例:
short a = 3;
a = a+2;//此行会报错
a +=2;
原因:2默认为int型,而扩展赋值运算符自带隐式转换。
5.条件运算符
语法:
(条件表达式)?表达式1:表达式2;
条件表达式为true,运算后的结果是表达式1;条件表达式为false时,运算后的结果是表达式2。
注:如果运算后的结果赋给新的变量,要求表达式1和表达式2为同种或兼容的类型。
6.位运算符
日常开发中位运算使用相对较少,但是巧妙的使用位运算可以大量减少运行开销, 优化算法。
<< 空位补0,被移除的最高位丢弃,空缺位补0
>> 被移位的二进制最高位是0,右移后,空缺位补0; 最高位是1,空缺位补1
>>> 被移位二进制最高位无论是0或者是1,空缺位都用0补
& 二进制位进行&运算,只有1&1时结果是1,否则是0
| 二进制位进行 | 运算,只有0 | 0时结果是0,否则是1
^ 相同二进制位进行 ^ 运算,结果是0,例:1^1=0 , 0^0=0
不相同二进制位 ^ 运算,结果是1,例:1^0=1
~ 正数取反,各二进制码按补码各位取反
负数取反,各二进制码按补码各位取反
十、控制台输入(Scanner)
Java提供Scanner类来实现从控制台向程序输入信息功能。
具体步骤:
创建Scanner类型对象,例:
Scanner sacnner=new Scanner(System.in);
此时编译软件会自动导入Scanner的包
import java.util.Scanner;
调用相关功能,来获取输入的信息
import java.util.Scanner;
public class scanf {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String ctrl = scanner.next();
int str2 = scanner.nextInt();
//此处省略其他
System.out.println(ctrl);
System.out.println(ctr2);
}
}
十一、控制语句
1.条件语句
(1)if语法:
基本语法:
if(判断条件1){
//条件成立时运行语句
}
else if(判断条件2){
//条件成立时运行语句
}
else{
//条件成立时运行语句
}
举例:
import java.util.Scanner;
public class pan_duan {
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
System.out.println("请输入需要判断的月份");
int month=scanner.nextInt();
if(month==3||month==4||month==5)
System.out.println(month+"是春季");
else if(month==6||month==7||month==8)
System.out.println(month+"是夏季");
else if(month==9||month==10||month==11)
System.out.println(month+"是秋季");
else
System.out.println(month+"是冬季");
}
}
当if语句块只有一行语句时,可以省略{},就如本例,但不推荐这样使用
(2)switch语法
基本语法:
switch(判断表达式){
case 值1:
//条件成立时运行语句
case 值2:
//条件成立时运行语句
……
case 值n:
//条件成立时运行语句
default:
//条件都不成立时运行语句
}
例:
import java.util.Scanner;
public class biancheng5 {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.print("请输入年份:");
int year = scan.nextInt();
System.out.print("请输入月份:");
int month = scan.nextInt();
System.out.print("请输入日期:");
int day = scan.nextInt();
int days = 0;
switch (month){
case 12:days += 30;
case 11:days += 31;
case 10:days += 30;
case 9:days += 31;
case 8:days += 31;
case 7:days += 30;
case 6:days += 31;
case 5:days += 30;
case 4:days += 31;
case 3:
if((year%4==0&&year%100!=0)||year%400==0){
days += 29;
}
else{
days += 28;
}
case 2:days += 31;
case 1:days += day;
}
System.out.println(year+"年"+month+"月"+day+"天是今年的第"+days+"天");
}
case 值相当于if的判断条件中的”==“,需要注意的是,case语句并没有{},但case语句具有”穿透性“,在一般情况下,需要用break取消其穿透性。并且凡是switch可以实现的功能 if else都可以实现,反之则不一定。switch和if else都能实现的功能建议使用switch,效率更高
下面是用if{}else语句实现相同功能的代码
import java.util.Scanner;
/**
* 编程一个程序,计算今天是今年的第几天.
* 例如: 控制台分别输入年月日
* 2023
* 10
* 30
*/
public class years_day {
static Scanner scanner=new Scanner(System.in);
public static void main(String[] args) {
System.out.println("请输入年份");
int year=scanner.nextInt();
System.out.println("请输入月份");
int month=scanner.nextInt();
System.out.println("请输入日期");
int days=scanner.nextInt();
boolean flag=run_nian(year);
int yearsDay=days(flag,month,days);
System.out.println(year+"年"+month+"月"+days+"日"+"是"+year+"年的第"+yearsDay+"天");
}
public static boolean run_nian(int year){
if(year%4==0&&year%100!=0||year%400==0)
{
return true;
}
else
return false;
}
public static int days(boolean flag,int month,int days){
int february;
int day=0;
if(flag){
february=29;
}
else{
february=28;
}
for(int i=1;i<month;i++){
if(i==1||i==3||i==5||i==7||i==8||i==10||i==12){
day+=31;
}
else if (i==2){
day+=february;
}
else{
day+=30;
}
}
day+=days;
return day;
}
}
2.循环语句
(1)while循环
基本语法:
while(条件表达式){
//条件成立时运行语句
}
//继续执行后续代码
while
循环在每次循环开始前,首先判断条件是否成立。如果计算结果为true
,就把循环体内的语句执行一遍,如果计算结果为false
,那就直接跳到while
循环的末尾,继续往下执行。
(2)do-while循环
基本语法:
do{
//条件成立时运行语句
}while(条件表达式);
//继续执行后续代码
(3)for循环
基本语法:
for(计数器初值;条件表达式;计算器操作){
//条件成立时运行语句
}
//继续执行后续代码
for
循环的功能非常强大,它使用计数器实现循环。for
循环会先初始化计数器,然后,在每次循环前检测循环条件,在每次循环后更新计数器。
3.break和continue
在循环过程中,可以使用break
语句跳出当前循环。在Java中,可以通过标签跳出多层循环,例:
public class manyxh {
public static void main(String[] args) {
outloop:for (int i = 0;i < 5;i++){
for (int j = 0;j <3;j++){
System.out.println("@");
if(i == 2){
break outloop;
}
}
System.out.println("#");
}
}
}
break
会跳出当前循环,也就是整个循环都不会执行了。而continue
则是提前结束本次循环,直接继续执行下次循环。
十二、面向对象
(一)面向对象介绍
面向对象(oop)和面向过程(pop)都是语言设计思想(宏观)
面向过程(具体的步骤):做一件事情,分析有哪些步骤,去一一实现
早期的编程语言,结构简单
典型类型为c语言
● 面向过程的程序设计思想(procedure -Oriented Programming),简称POP
● 关注的焦点是过程:过程就是操作数据的步骤。如果某个过程的实现代码重复出
现,那么就可以把这个过程抽取为一个函数。这样就可以大大简化冗余代码,便
于维护。
● 典型的语言:C语言
● 代码结构:以函数为组织单位。
● 是一种“执行者思维”,适合解决简单问题。
分析出解决问题所需要的步骤,然后把步骤一步一步实现。
扩展能力差、后期维护难度较大
现在大多数语言都是面向对象:c++,c#,java,python....
面向对象(描述类的属性):从宏观上分析程序有哪些功能,然后对功能进行分类,把不同的功能封装在不同的类中。是一种宏观的设计,但在具体实践中仍然需要面向过程,两者相辅相成
面向对象的程序设计思想( Object Oriented Programming),简称OOP
● 关注的焦点是类:在计算机程序设计过程中,参照现实中事物,将事物的属性特
征、行为特征抽象出来,用类来表示。
● 典型的语言:Java、C#、C++、Python、Ruby和PHP等
● 代码结构:以类为组织单位。每种事物都具备自己的属性和行为/功能。
● 是一种设计者思维,适合解决复杂问题。代码扩展性强、可维护性高
面向对象的具体实现(一)
类:具有相同特征的事物的抽象描述(抽象的,概念的)
对象:由类这一模型所创造的具体存在的实例(具象的,具体的)
什么是对象:对象是类的实例,是以类为模板在内存中创建出实际存在的实例(对象)
如何创建对象:使用new关键字 new Car();--类的构造方法,类会自动提供的 Car bm=new Car();
Car bm为声明变量,new Car()是构造方法,=把右边创建的对象赋给左边的变量,左侧变量则可以表示对象
类和对象的关系:类是模板,对象是具体实例,万事万物皆为对象
现实生活中先有对象,后有类。编程中先设计类后创建对象
例如String是类,String str;中str是对象
类的组成:变量——事物属性的描述
方法——事物的行为
构造方法——初始化对象
块——一段没有名称的代码块
内部类——类中声明的类
类的构建
第一步 发现某一类
第二步 定义类的具体属性
第三步 定义类的成员方法
第四步 类名调用
访问修饰符有两种public,无(默认)
修饰符:final,abstract
关键字class用来定义一个类
(二)成员变量(名词 理解为属性):直接定义在类中,也成为类的成员
成员变量不进行初始化时,系统会自动初始化赋值
引用类型-null
float-0.0
int long-0
char-' '
boolean-false
(三)成员方法
动词->行为,功能
(四)类的构造
特点:方法与类名相似,没有返回值,不需要用void修饰,每一个类中都有个默认的构造方法
作用:是用于初始化赋值
public Person(){//构造方法
name =null;
age=0;
gender=null;
money=0;
(五)方法重载
方法重载:在一个类中有多个重名的方法
如何区分调用的方法:通过方法的参数个数,类型,顺序区分
构造方法和成员方法都可以实现方法重载
方法重载与返回值无关
(六)引用
引用方式: 值传递--基本类型作为参数传递 引用传递——引用类型作为参数传递 由于引用类型只开辟了一块地址函数内外的参数共用一块地址 形参的值的改变会影响实参的值、 确定是值传递还是引用传递,要看调用函数时括号里是基本类型还是引用类型
public class cite1 {
public static void main(String[] args) {
int a =10;
cite1 test1=new cite1();
test1.test(a);//20 实参传进去 填充形参
System.out.println("a="+a);//10 输出实参
}
public void test(int b){
b=20;
System.out.println("b="+b);//20
}
}
(七)this关键字
this关键字:
public class Person {
String name;
int age;
String gender;
public Person(String name,int age,String gender){
this.name=name;
this.age=age;
this.gender=gender;
}
this表示当前正在使用对象的构造方法
public Person(){
this(null,0,null);
}
this表示当前正在使用对象的成员变量
public void showInfo(){
System.out.println("姓名:"+name+"年龄:"+age+"性别:"+gender);
this.work();
}
this也可以表示调用当前使用对象的成员方法
public void work(){
System.out.println(name+"正在工作");
}
}
(八)代码块
代码块是在类声明中一段没有名字的代码。其分为静态代码块和实例代码块两种。
实例代码块的格式是
{
}
特点是在创建时不需要显示的调用就可以执行
静态代码块的格式是
static{
}
其特点是在类被加载时就会执行。
package com.wbc.opp.oop.day2.codeBlock;
public class codeBlockDemo1 {
{
System.out.println("我是实例代码块,在创建时,不需要显示的调用,可以直接执行");
}
static{
System.out.println("我是静态代码块,在类被加载时就会执行");
}
/*public static void main(String[] args) {
//静态代码块会加载
}*/
static int a;
}
(九)static静态
static被称为静态,可以用于修饰类的成员变量,成员方法,代码块,内部类
静态成员不依赖于类的实例(对象),被类的所有实例共享
static修饰的方法、变量不需要要依赖于对象来进行访问,只要这个类被加载,静态成员
就会被加载创建
特点:
• 随着类的加载而加载
• 优先于对象存在
• 修饰的成员,被所有对象所共享
• 可不创建对象,直接使用类名调用
public class Chinese{
String name ;//姓名
static String country=”中国”;//国家
}
静态属性是类的所有对象共享的,即不管创建了多少个对象,静态属性在内存中
只有一个
可以通过类名.静态变量直接访问,也可以通过对象.静态变量的方式访问(但
是更推荐使用类名.静态变量的方式)。
//静态的成员在类被创建时就创建了,不需要创建对象
静态的成员变量对应静态的成员方法
非静态对应非静态
//静态的方法只能使用静态的成员变量,因为他们都是随着类的加载而加载的
//与非静态成员变量没有关系的,每次入参去完成固定功能的,适合定义静态变量
//一般功能类的类都定义为静态的
静态的方法是否能使用this关键字呢?->不能,静态方法与对象随着类的加载就加载了,与对象无关
静态方法不可以用this,也不能用super
静态成员变量与非静态成员变量的区别
静态成员变量相较于非静态成员变量,不依赖类的实例也就是对象,被类的所有实例共享(静态成员变量开辟的内存只有一份,每一个对象对应的访问静态成员变量使其值改变时,其内存的值也就改变)。当类加载的时候, 静态成员变量就会加载,并且可以不创建对象,可以直接使用类名调用。静态成员变量能对应静态成员方法, 因为静态成员变量与静态方法都是随着类的加载而加载的,也可以对应非静态成员方法。而非静态成员变量只 能对应非静态成员方法。静态成员变量不能使用this和super关键字,而非静态成员变量可以。
package com.wbc.opp.oop.day2.staticDemo;
public class Chinese {
String name;//中国人每人都需要有自己的属性
static String country = "China";//静态属性在内存中只有1份
//使用一个变量记录此类被创建了多少个对象
static int count=0;
public Chinese(){
count+=1;
}
//静态的方法只能使用静态的成员变量,因为他们都是随着类的加载而加载的
//与非静态成员变量没有关系的,每次入参去完成固定功能的,适合定义静态变量
//一般功能类的类都定义为静态的
public static void printCount(){
System.out.println("共创建了"+count+"个对象");
}
public void eat(){
System.out.println(name+"吃饭");
System.out.println(count);//非静态的方法可以使用非静态的变量
}
}
package com.wbc.opp.oop.day2.staticDemo;
public class testChinese {
public static void main(String[] args) {
//静态的成员在类被创建时就创建了,不需要创建对象
System.out.println(Chinese.country);
Chinese c1 =new Chinese();
c1.name="张三";
Chinese c2=new Chinese();
c2.name="李四";
c1.country="中国";
c2.country="中国人";
System.out.println(c1.country);//中国人
System.out.println(c1.country);//中国人
//static属性的成员变量,所有对象共用一份,减少了占用空间
//在访问时,建议使用类名访问
//静态的成员在类被创建时就创建了,不需要创建对象
System.out.println(Chinese.country);
//对象计数器
Chinese.printCount();
}
}
(十)包
包: 产生问题:类的数量变多,会出现同名类,需要按照不同的功能分类
解决问题:提出包的概念,用来管理类
包的又一重要功能,权限访问
第一级 指该项目的类型,如com,org,gov等,
第二级 指项目所开发或者运行的公司名称,如:oracle,sun,huawei等
第三级 指项目的名称,如:bcms,oa,erp,cms等
第四级 指项目模块的名称,如:bean,action,exception
(十一)import
关键字 import 当在一个类中使用了另一个包中的类,使用前必须使用import关键字调用 使用本包中的类,以及java.long包中的类,不需要导入 import在导入时只能导入相同的一个类名 可以使用全命名 new 包名.包名.。。。.类名
(十二)访问修饰符
访问修饰符共有四种,由大到小分别是:
public 公共权限,用来修饰类,成员方法,成员变量,内部类。 public 修饰的成员可以访问到本 类,本包其他类,其他包。
protected 受保护权限,修饰成员变量,成员方法,内部类。 protected 修饰的成员可以访问到本 类,本包不同包的子类(通过extends继承父类的子类)。
默认权限(什么都不写)修饰类,成员变量,成员方法,内部类。默认权限 修饰的成员可以访问到本 类,本包。
private 私有权限,修饰成员变量,成员方法,内部类。 private 修饰的成员可以访问到本类。
package com.wbc.opp.oop.day2.accessDemo;
public class Animal {
public String proname0;//公共权限修饰的成员在任何地方都可以访问到
protected String proname1;//受保护权限 在本类以及同包的其他类中可以访问
String proname2;//默认权限 在本类以及同包的其他类中可以访问
private String proname3;//私有权限 只能本类使用
public void pubtest(){
Animal animal=new Animal();
animal.proname0="aaa";
animal.proname1="bbb";
animal.proname2="ccc";
animal.proname3="ddd";
}
//公共 受保护 默认 私有都可以修饰内部类
}
(十三)面向对象三大特征之封装
一般意义的封装:把一段重复代码抽取成一个函数,称为代码的封装(包装)
面向对象语言的封装:将类的某些信息隐藏在类的内部(通过使用不同的访问权限修饰符),不许外部程序直接访问
而是通过该类提供的方法来实现对隐藏信息的操作和访问
封装案例1:将成员变量私有化
封装案例2:将成员方法私有化
java固定模式:解决某种问题的固定方式(算法)
单例模式:让一个类在一个程序中只能创建一个对象(window_TestWindow)
将类的构造方法私有化,外界不可以随便调用,向外界提供一个方法去构建
在类中new一个static的自己,保证在类生成时,就生成了唯一的对象
补充变量的分类:
1、数据类型:基本数据类型、引用数据类型
2、按照位置分:
成员变量:在定义类中,可以使用权限修饰符修饰;
在构造方法中进行自动赋值;
生命周期不同:静态(随着类的加载而生成,类的销毁而销毁)
非静态(随着对象的创建,销毁而创建销毁)
存储位置:
静态:方法区
非静态:堆
局部变量:在定义方法中,不能使用权限修饰符修饰
必须自己进行初始化
生命周期:随着方法调用而生成,方法调用结束而结束
存储位置:栈
例:
package com.wbc.opp.oop.day3.packAge;
public class Person {
private String name;//私人权限
private int age;//私人权限
//定义公共的成员方法,可以在此类方法中加入控制语句
// 外界通过调用方法来访问成员变量
public Person(){
}
//通过构造方法
public Person(String name,int age){
this.name=name;
this.age=age;
}
public void setName(String name){
if(name.length()>2&&name.length()<6){
this.name=name;
}
}
public String getName(){
return name;
}
public void setAge(int age){
if(age<150&&age>0){
this.age=age;
}
}
public int getAge(){
return this.age;
}
}
package com.wbc.opp.oop.day3.packAge;
public class TestPerson {
public static void main(String[] args) {
Person p1=new Person();
/*
String name;在其他类中可以直接对类中的属性进行赋值操作,但没法控制其赋值的内容
p1.name="sdadsa";
*/
p1.setName("abac");
String p1Name= p1.getName();
System.out.println(p1Name);
p1.setAge(2);
int p1Age=p1.getAge();
System.out.println(p1Age);
}
}
(十四)面向对象三大特征之继承
继承就是将同一类事物中的共性进行抽取,定义在一个类中(基类)
其他类可以继承基类,就能拥有基类中的功能,实现代码的复用类
以及可以在子类中扩展属于子类自己的功能而不影响其他类
例:猫狗都是动物,所以可以抽取一个动物类,以继承功能
优点:提高了代码的重复率,减少了冗余。
子类使用extends关键字继承父类
一个类只能继承一个类,但可以多层继承(继承体系)
Object是所有类的父类,是java类体系中最顶尖的类
当一个类没有显示继承,默认继承Object
dog继承Animal,Dog称为子类,派生类;Animal成为父类
方法覆盖:可能有些功能比较笼统或者与具体有差距,需要更加细致的功能。
所以出现了方法的覆盖
当父类中方法的实现不能满足子类需求是,可以在子类中对父类的方法进行覆盖
要求:子类重写的方法结构与父类方法结构一致(权限大于等于父类权限;返回值,名字,参数必须一致)
父类的私有方法不能重写,挎包的默认权限方法不能重写
子类抛出的异常不能大于父类的异常
@Override//注解标签。表示此方法是从父类重写来的,检查重写是不是正确
可以不用添加,只需要重写正确即可,但建议保留(编译器可以进行语法验证;增加可读性)
super关键字:当覆写后子类需要调用父类的方法,可以用super关键字调用
例如
@Override
public void sleep() {
super.sleep();//使用super调用父类方法
System.out.println("哮天犬不需要睡觉");
}
super不仅可以调用父类,还可以调用父类的父类
super与this相似,this表示当前的对象,super代表父类的内存空间的标识
this在用的时候需要创建本类对象,但super没有创建父类对象,只是将父类的信息存入子类
继承中的构造方法:
在子类继承父类时不会继承构造方法,只能通过super构造
在子类的构造方法首行调用父类的构造方法-> super(无参调无参,有参调有参);
不写会默认无参构造(super();)
常见错误:默认无参构造时,父类需要有无参构造方法
例:
Animal是父类
package com.wbc.opp.oop.day3.inherit;
public class Animal {
private String breed;//品种
private String name;
private int age;
private String gender;
public String getBreed() {
return breed;
}
public void setBreed(String breed) {
this.breed = breed;
}
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 String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public void printInfo(){
System.out.println("姓名:"+name);
System.out.println("品种:"+breed);
System.out.println("性别:"+gender);
System.out.println("年龄"+age);
}
public Animal() {
}
public Animal(String name, int age, String gender,String breed) {
this.name = name;
this.age = age;
this.gender = gender;
this.breed=breed;
}
public void sleep(){
System.out.println(breed+":"+name+"正在睡觉");
}
public void eat(String food){
System.out.println(breed+":"+name+"正在吃"+food);
}
}
cat是子类之一
package com.wbc.opp.oop.day3.inherit;
public class Cat extends Animal{
public void catchMice(){
System.out.println(getName()+":正在抓老鼠");
}
}
package com.wbc.opp.oop.day3.inherit;
public class TestCat {
public static void main(String[] args) {
Cat cat=new Cat();
cat.setName("Tom");
cat.setBreed("抓老鼠的猫");
cat.setGender("公");
cat.setAge(6);
cat.catchMice();
cat.printInfo();
}
}
dog是另一子类
package com.wbc.opp.oop.day3.inherit;
public class Dog extends Animal {
public void lookHouse(){
System.out.println(getName()+":正在看家");
}
@Override//注解标签。表示此方法是从父类重写来的,检查重写是不是正确
public void sleep(){
System.out.println("狗在睡觉");
}
}
package com.wbc.opp.oop.day3.inherit;
public class TestDog {
public static void main(String[] args) {
Dog dog=new Dog();
dog.setName("viky");
dog.setBreed("泰迪");
dog.setGender("母");
dog.setAge(6);
dog.lookHouse();
dog.printInfo();
dog.eat("狗粮");
}
}
哮天犬又是dog的子类
package com.wbc.opp.oop.day3.inherit;
public class Xtd extends Dog{
public void fly(){
System.out.println("哮天犬可以飞");
}
//可以左键快速重写
@Override
public void sleep() {
super.sleep();//使用super调用父类方法
System.out.println("哮天犬不需要睡觉");
}
}
package com.wbc.opp.oop.day3.inherit;
public class TestXtq {
public static void main(String[] args) {
Xtd xtd=new Xtd();
xtd.setName("哮天犬");
}
}
(十五)抽象类
抽象方法:只有方法声明,没有具体实现的方法
why要有抽象方法:在一些体系结构的顶端类中,有些功能没必要实现
因为在不同子类中的实现都不相同,这时候就可以将方法声明为抽线方法
public abstract void eat(); //定义为抽象方法,没有具体实现,不完整
此时需要将类定义成抽象类
public abstract class Animal {//将类使用abstract抽象化
使用abstract关键字修饰的类就是抽象类。
若一个类中有抽象方法,则其必须是抽象类。
但抽象类中也可以有非抽象方法
抽象类:一个类中有不完整的方法,则这个类是抽象类
特点:除了其不可构造对象外,其他功能与其他正常的类都相同,可以有变量,方法,构造器
其子类必须重写其所有抽象方法,或者也定义为抽象类
作用:主要在上层定义功能,让子类继承实现
语法:public abstract class Animal {//将类使用abstract抽象化
例:
父类Animal(抽象类)——>子类 cat(抽象类)
——>另一子类 dog(非抽象)
package com.wbc.opp.oop.day3.Abstract;
public abstract class Animal {//将类使用abstract抽象化
private String name;
private int age;
public abstract void eat(String food); //定义为抽象方法
public Animal(){
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
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 void printINfo(){
System.out.println(name+"\t"+age);
}
}
package com.wbc.opp.oop.day3.Abstract;
public abstract class cat extends Animal {//定义为抽象类
}
package com.wbc.opp.oop.day3.Abstract;
public class Dog extends Animal{
public Dog() {
}
public Dog(String name, int age) {
super(name, age);
}
@Override
public void eat(String food) {//重写实现
System.out.println(getName()+"吃"+food);
}
}
package com.wbc.opp.oop.day3.Abstract;
public class TestDog {
public static void main(String[] args) {
Dog dog=new Dog();
dog.setName("旺财");
dog.setAge(10);
dog.printINfo();
dog.eat("狗粮");
}
}
(十六)面向对象特征三大特征之多态
父类引用指向子类的对象,从而产生多种形态 例: Animal dog = new Dog(); 同一种事物,在不同的时刻表现不同的状态 前提:二者存在直接或间接的继承关系时,
/*
//装狗
public void putDog(Dog dog){
System.out.println("把狗装进冰箱");
}
//装猫
public void putCat(Cat cat){
System.out.println("把猫装进冰箱");
}
传统方法,不利于程序扩展,每添加一个动物就需要添加一个方法,如果动物多了很麻烦
//装动物
public void putAnimal(Animal animal){
System.out.println("把"+animal+"装进冰箱");
}
用父类类型表示任意的子类类型对象,利于程序扩展
*/
父类引用子类的对象,即形成多态
用法:先用父类类型定义子类类型的对象 然后就可以用用父类类型表示任意的子类类型对象 利于程序扩展 对于成员方法: Animal dog =new Dog(); Animal cat = new Cat(); dog.eat();//编译时调用的是Animal的方法,因为定义的是Animal类 // 实际运行的时候调用的是dog/cat的方法,因为将Dog/Cat new出来了 //编译看左边(Animal),运行看右边(new Dog();) cat.eat(); 不能调用子类本身有的,但父类没有的方法 对于静态成员方法 全都看左边,只能调用父类的静态成员方法(因为静态不存在重写) 对于成员变量: 只调用父类的成员变量 /* 虽然多态理由扩展,但也存在问题: 一旦转为父类类型,就调用不了子类本身特有的方法,只能调用父类的方法 可以数据转换弥补(向下转型) Dog d = (Dog) dog; 需要有继承关系 需要注意的是,在方法中强制转换时,需要注意的时其他子类无法转换成同级子类 可以在方法中使用if控制语句 // public void putAnimal(Animal animal){ System.out.println("把"+animal+"装进冰箱"); animal.eat();//Animal类的方法 if(animal instanceof Dog){//判断 Dog dog=(Dog)animal;//强制转换 dog.lookHome();//DOg类的方法 } } // instanceof 表示父类类型中持有的实际类型(animal)是否为子类类型(Dog) 父类与子类类型相同返回true 不同返回false */
(十七) final 关键字
final int a=10;定义常量{属性(成员变量)}、修饰类、方法、参数
final修饰类:不能被定义为抽象类或接口,不能被继承
例如:java.String类被final修饰,不能被继承 final修饰方法:不能被重写
final修饰参数:初始赋值后不能再被改变
情况1、在类确定时值就确定,则直接赋值,赋值后值无法改变
所以建议用static修饰:final static 数据类型 成员变量
情况2、在类定义时值不明确,需要在构造方法中对其赋值
因为成员变量值不能改变,所以称为常量
常量一般建议全部使用大写字母,多个字母使用下划线(例 MAX_VALUE)
(十八)接口
接口可以看作一种特殊的抽象类,里面也可以包含抽象方法
接口不能被创建对象,被其它类实现,重写抽象方法
主要也是用来定义功能的
java中的接口如何定义:jdk8之后可以定义常量、抽象方法、静态方法、默认方法
public interface InterfaceDemo {
int num=10;//常量 public static final int num=10;
void eat();//默认为public abstract void eat();
public static void test(){
//可以直接通过接口名调用
System.out.println("可以直接通过接口名调用");
}
default void test1(){
//默认方法 被子类继承后调用
}
}
类通过 implements关键字实现接口,并且将抽象方法重写 类实现接口,要不重写接口的抽象方法,要么定义成抽象类,由他的子类再去实现
public class Class implements InterfaceDemo{
@Override
public void eat() {
}
}
一个接口可以继承多个接口,一个类可以实现多个接口 public interface Interface3 extends Interface,Interface2{ } 默认方法被子类继承使用
public class Demo implements Interface3{
@Override
public void eat() {
}
@Override
public void sleep() {
}
@Override
public void test1() {
}
public static void main(String[] args) {
Demo demo=new Demo();
demo.test1();
}
}
十三、java中的常用类
(一)object类
Object是java中所有类的父类,体系结构中最顶层的类,位置是java.lang.Object
Object可以表示java中任意的类,体现了多态性
方法:
1、toString();
输出对象对象在内存里存储,无法输出对象
当输出一个对象时,会默认调用此对象的toString()方法,对象以字符串形式输出
Object中的toString输出的是哈希值(int型)
可以自己重写toString();方法
我们可以对Object类中的toString();进行重写,在后面调用时就会调用自己的toString();
2、equals();
public boolean equals(Object obj);指示一些其他对象是否等于此。
(二)Arrays类
1、equals();
比较内容是否一致
2、copyOf();数组复制
3、fill();
用指定值将指定数组中的每个元素填充
4、toString();
将数组中的元素内容拼接为字符串输出
5、sort();
会根据数组的长度和类型自动选择算法和方法排序 也可以给定区间排序sort(数组名,开始位置索引,结束位置索引-1)
如果一个类需要排序,那么这个类需要实现Comparable接口去指定一个排序的方法
当字符串之间比较是,使用compareTo()方法
当引用类型数组想要排序时,可以按照下面所示方法设定排序标准
6、binarySearch();
二分查找:binarySearch(数组名,查找元素);查找到返沪索引,没找到返回-1 使用前提:数组有序,可以在查找前使用sort();排序
(三)基本类型包装类:
封装基本数据类型,并为其提供一系列操作方法
继承关系:java.lang.Integer->java.lang.Number->java.long.Object
构造方法:int,String(必须是数字)
public final class Integer extends Number implements Comparable<Integer>
实现了Comparable接口
例:Integer--->int的基本类型包装类
(四)String
1、解释:
String类位于JAVA中的java.lang.String包中,java中所有的字符串都是此类的实例
其底层代码是将char数组进行封装操作,例如
"abc"--->是字符串对象,底层是 private final char value['a','b','c'];
2、字符串的创建方式
1)、 String s="abc";需要注意的是,按照此方法创建两个值一样的字符串时,不会创建新的String类对象,而是调用旧的地
String s ="abc";
String ss="abc";
System.out.println(s==ss);//true
2)、String s1=new String("abc");这样创建的是两个不同的字符串
String s1=new String("abc");
String s2=new String("abc");
System.out.println(s2==s1);//false
3)、字符串的值不可改变 但是我们日常写代码时,字符串可以添加,原理如下
//public final class String
String s3="abcd";
s3+="efg";//创建新的字符串"abcdefg"
s3+="1111";//再次创建新的字符串"abcdefg1111"
3、String类的方法
1、四种构造方法+对象.getBytes();+对象.getCharArray();
构造方法
String();
String(String s)
String(byte[] byte)
String(char[] chars)
*/
String s1 =new String();//无参构造
String s2 =new String("abc");//有参构造
//现代传输为数字话传输
String s3="你好";
//明码编码
byte[] ss3=s3.getBytes();//String类的方法,将字符串转为byte类型
//解码 字节数组转为字符串
String s4=new String(ss3);//String(byte[] byte)
System.out.println(s4);
//当使用特殊的编码时 函数后面+throws UnsupportedEncodingException
byte[] sss3=s3.getBytes("GBK");//中文编码
String ss4=new String(sss3, "GBK");//String
System.out.println(ss4);
//当想要用Arrays的方法操作字符串时
String str="cba";
char[] chars=str.toCharArray();//将字符产转换成字符数组
Arrays.sort(chars);//可以使用数组的方法,例如此例 字符数组排序
String s5=new String(chars);//需要定义新字符串接收
System.out.println(s5);
2、六种判断功能
/*
判断功能
boolean equals(Object anObject)
boolean equalsIgnoreCase(String anotherString)
boolean contains(CharSequence s)
boolean startsWith(String prefix)
boolean endsWith(String suffix)
boolean isEmpty
*/
String s10="abcd";
String s20="abcD";
System.out.println(s10.equals(s20));//比较是否一致
System.out.println(s10.equalsIgnoreCase(s20));//忽略大小写比较
System.out.println(s10.contains("ab"));//判断是否包含指定的子串
System.out.println(s10.startsWith("ac"));//判断是否为指定的字符串开头
System.out.println(s10.endsWith("cd"));//判断是否以指定的字符串结尾
System.out.println(s10.isEmpty());//判断字符串是否为空
3、获取功能
/*
获取功能
int length() 获取字符串长度
char charAt(int index)获取指定位置上的字符
int indexOf(String str)返回字符串首次出现的位置
int indexOf(String str, int fromIndex) 从指定位置开始查找指定字符
int lastIndexOf(String str)//从后向前找
int lastIndexOf(String str, int fromIndex)//指定位置从后往前找
String substring(int beginIndex)从起始位置开始截取后面打印
String substring(int beginIndex, int endIndex)从指定范围内截取打印
*/
String str1="abcdefg";
System.out.println(str1.length());//获取长度,长度包括空格等
System.out.println(str1.charAt(2));//返回索引位置上的字符
System.out.println(str1.indexOf("c"));//返回查找字符的索引
System.out.println(str1.indexOf("c",3));//从指定位置开始查找
System.out.println(str1.lastIndexOf("c"));//从后向前找
System.out.println(str1.lastIndexOf("c",7));//从指定位置从后往前找
System.out.println(str1.substring(1));//从起始位置开始截取后面打印
System.out.println(str1);
System.out.println(str1.substring(0,6));//从指定范围内截取打印
4、转换功能
byte[] getBytes(); 将字符串转为byte类型
char[] toCharArray(); 将字符串转为字符数组
String valueOf(int i) 将int转String
String valueOf(char data[]) 将char[]转String
String toLowerCase()//转小写
String toUpperCase()//转大写
String concat(String str)将str拼接在原字符串后
String[] split(String regex)//将字符串按照指定的分割符号拆分 并存入字符串数组
使用方法
String str2 =String.valueOf(1000);
System.out.println(str.charAt(1));//0
int a=Integer.parseInt(str2);//String 转 int
System.out.println(a);
String str3=String.valueOf("abc".toCharArray());//将"abc"转为数组再转会字符串
System.out.println(str3);
String str4="abcAd";
System.out.println(str4.toLowerCase());//转小写
System.out.println(str4.toUpperCase());//转大写
System.out.println(str4.concat(str3));//拼接
String str5="ab;cdE;FG";
String[] strings=str5.split(";");//将字符串按照指定的分割符号拆分 并存入字符串数组
System.out.println(Arrays.toString(strings));//[ab, cdE, FG]
5、替换功能
String replace(CharSequence target, CharSequence replacement)
//将字符串中指定的所有字符替换成新的指定字符
String replaceAll(String regex, String replacement)
//通过正则表达式匹配将同一类全部替换 "\\d"表示数字,"[a-z]"表示字母
String replaceFirst(String regex, String replacement)
//通过正则表达式匹配将该类第一个出现的字符替换
String trim()
//消除字符串两边的空格 中间无法消除
//如果想要消除中间的可以用replace替换
String Str =" abcdefg123 ";//将小写的c用大写的C替换
System.out.println(Str.replace("c","C"));//将小写的c用大写的C替换
System.out.println(Str.replaceAll("\\d","C"));//通过正则表达式将同一类全部替换
System.out.println(Str.replaceFirst("\\d","e"));//将第一个数字替换成e
System.out.println(Str.length());//由空格 长度12
System.out.println(Str.trim().length());//消除两侧空格 无空格 长度10
(五)StrinigBuffer类
1、解释
StringBuffer可以更好的进行字符串拼接,相比String类单纯拼接更加省时省空间。StringBuffer是线程安全的可变字符序列、是内容可以改变的字符串
StringBuffer底层是char[] value,String类底层是private final char value[],StringBuffer可改变的原因就是没有final修饰。每次添加内容都是对此数组进行操作,不会创建新的对象
当字符数组满了之后会再StringBuffer类内部创建新的数组对象。需要注意的是StringBuffer类型的字符串在调用操作时的改变会影响自身的值
2、方法
1、构造方法
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
public StringBuffer() {
super(16);
}
public StringBuffer(int capacity) {
super(capacity);
}
//创建一个内容长度+16的字符数组
StringBuffer s1=new StringBuffer("abcd");
s1.append("efg");//+efg,但不创建新的数组
StringBuffer s2=new StringBuffer();//空参数组长度为16
//StringBuffer s1=new StringBuffer("abcd");实参构造为内容长度+16的字符数组
StringBuffer s3=new StringBuffer(100);//可以指定数组长度
构造分为三种方法,分别是无参构造(),传入字符串构造,传入长度构造。空参构造时默认长度为十六,传入字符串构造时,默认长度为字符串长度+16.
2、插入
StringBuffer insert(int offset, String str)
StringBuffer append(String str)
例:
System.out.println(s1);//abcdefg
s1.insert(1,"a");//想指定位置插入
s1.append("ab");//向末尾插入
System.out.println(s1);//aabcdefgab
插入分为两种方法,insert是指定位置插入,append是向末位插入,两种方法都是非静态方法,需要创建对象之后调用。
3、删除
StringBuffer deleteCharAt(int index)
StringBuffer delete(int start, int end)
例:
s1.deleteCharAt(1);//指定位置删除
System.out.println(s1);//abcdefgab
s1.delete(0,3);//删除指定区间的元素 0,1,2位置的元素
System.out.println(s1);//defgab
删除同样是两种方法,deleteCharAt是指定位置删除,需要传入索引值;delete是删除区间元素,需要给出索引范围。两种方法都是非静态方法,需要创建对象之后调用。
4、替换
//StringBuffer replace(int start, int end, String str)在区间内替换
例:
s1.replace(0,3,"aaa");
System.out.println(s1);//aaagab
替换只有一种方法,replace传入范围和替换的元素,完成替换。这是非静态方法,需要创建对象之后调用。
5、反转逆序
StringBuffer reverse()
例:
s1.reverse();
System.out.println(s1);//bagaaa
reverse是反转字符串的一种方法,将字符串逆序排列。这是非静态方法,需要创建对象之后调用。
(六)、StringBuilder类
其是多线程并发且线程安全的StringBuffer类,与StringBuffer用法大致相同,StringBuffer由于方法上没有加锁,在多任务执行时线程不安全,只适合单线程运行
(七)正则表达式
正则表达式 regex,全称Regular Expression。正则表达式是一种规则(模式)匹配语法
可以使用一些正则表达式中的特殊符号来定义一种规则,然后用此规则匹配某个字符,如果字符串与规则匹配则返回true,不匹配返回false。
其常用方法如下:
\\d 匹配数字 \\D匹配非数字 \\w 匹配数字字母下划线 \\W 匹配非数字 字母 下划线
\\s匹配空格 \\非空格 |逻辑或 .匹配任意字符 使用时需要转义 []表示范围 {}表示数量
*零或多 +一或多 ?最多1
大多常用情景可以在网上搜索生成拿来使用,本文提供相关网站为
https://www.sojson.com/regex/generate 生成regex
String s="a";
System.out.println(s.matches("b"));//定义s里是否有b
/*//数字*/
String s1="123456";
//定义s里只能有数字,但是数字只能有一次,[]表示范围
System.out.println(s1.matches("[0-9]"));
//*表示允许零次或多次出现数字
System.out.println(s1.matches("[0-9]*"));
//+表示至少一次出现数字
System.out.println(s1.matches("[0-9]+"));
//?表示至多一次出现数字
System.out.println(s1.matches("[0-9]?"));
//{num}必须有几次,本例中表示0-9必须有三位
System.out.println(s1.matches("[0-9]{3}"));
//第一位是1,后面三位数字
System.out.println(s1.matches("1[0-9]{3}"));
//{num1,num2}表示区间
System.out.println(s1.matches("[0-9]{3,6}"));
//只允许出现357
System.out.println(s1.matches("[357]{3}"));
// \\d表示数字、\\D表示非数字
System.out.println(s1.matches("\\d{3,6}"));
System.out.println(s1.matches("\\D{3,6}"));
//验证手机号
String phoneNum="13691309195";
System.out.println(phoneNum.matches("1[3578]\\d{9}"));
/*//字母*/
String eng ="abcdABCD";
System.out.println(eng.matches("[a-z]*"));
System.out.println(eng.matches("[A-Z]*"));
System.out.println(eng.matches("[a-zA-Z]*"));
//编码中A比a小
System.out.println(eng.matches("[A-z]*"));
//数字加字母加下划线
System.out.println(eng.matches("\\w*"));//允许
System.out.println(eng.matches("\\W*"));//不允许
/*//空格*/
String k =" ";
System.out.println(k.matches("\\s"));
//例子 :邮箱 xxxxxxx@xxx.com 或者xxxxxx@xxxx.com.cn
String s3 = "1790352490@qq.com";
System.out.println(
s3.matches("\\w{6,10}" +
// .有特殊含义 需要转义
"@[0-9A-z]{2,6}\\.(com|com\\.cn)")
);
(八)Date类和Calendar 类
1、Date类
Date类是 Java 中表示日期和时间的类,可以用来获取和设置日期和时间的各个部分,比如年、月、日、小时、分钟、秒等。Date 类也可以用于日期和时间的计算、比较和格式化。
方法:
1、构造方法:
Date date = new Date();
2、获取时间
System.out.println(date.getTime());
此时获取的时间是自1900年1月1日到构造方法运行时的毫秒数,通常需要定义1个long类型的变量来接收。
//要求:测试String字符串和StringBuffer字符串循环拼接10万次,程序运行耗时.
public static void main(String[] args) {
String string =new String("abc");
Date date1=new Date();
for (int i=0;i<100000;i++){
string +="a";
}
Date date2=new Date();
System.out.println("String 拼接十万次用时:");
System.out.println(date2.getTime()-date1.getTime());
StringBuffer stringBuffer=new StringBuffer("abc");
Date date3 =new Date();
date3.getTime();
for (int i=0;i<100000;i++){
stringBuffer.append("a");
}
Date date4 =new Date();
System.out.println("StringBuffer 拼接十万次用时:");
System.out.println(date4.getTime()-date3.getTime());
}
这时可以明显发现,StringBuffer拼接效率高于String类,这也源自与他底层没有final限制
2、toString方法
打印程序运行开始的那一时刻
3、getYear();getMonth();getDay();
这三个方法已启用需要自己手动“配平”
System.out.println(date.getYear()+1900);//2024
System.out.println(date.getMonth()+1);//3
System.out.println(date.getDay());//6 显示周几
非常难用
所以在使用这些功能时,推荐使用Calendar类的方法
3、时间戳
//传入运行时的毫秒数可以转回具体时间 1709376357241称为时间戳
Date date2 = new Date(1709376357241L);
System.out.println(date2);//Sat Mar 02 18:45:57 CST 2024
当构造函数时传入时间戳时,可以返回获取时间戳时的时间
时间戳就是我们在getTime时获取的一大串毫秒数
2、Calendar类
在用Date类获取时间时相当的麻烦,而且其相关方法都已经弃用了,这里Java给我们提供了更加方便的包,日历Calendar类
1、构造方法:
Calendar calendar=Calendar.getInstance();
2、相关方法
System.out.println(calendar.get(Calendar.YEAR));
System.out.println(calendar.get(Calendar.MONTH)+1);
System.out.println(calendar.get(Calendar.DAY_OF_MONTH));//今天是月的第几天
System.out.println(calendar.get(Calendar.DAY_OF_WEEK)-1);//是周的第几天,老外周日是第一天
System.out.println(calendar.get(Calendar.DAY_OF_YEAR));//年的第几天
System.out.println(calendar.get(Calendar.WEEK_OF_YEAR));//年的第几周
System.out.println(calendar.get(Calendar.WEEK_OF_MONTH));//月的第几周
//设置时间
calendar.set(year,month,date);
(九)Math类与Random类
见名知意,这两个类中为我们提供了数学与随机数的相关方法
System.out.println(Math.PI);//Π的值
System.out.println(Math.abs(-1));//求绝对值
System.out.println(Math.sqrt(9));//开方
System.out.println(Math.pow(2,3));//n次幂
System.out.println(Math.max(1,2));//返回最大值
System.out.println(Math.floor(9.9));//向下取整
System.out.println(Math.ceil(9.1));//向上取整
System.out.println(Math.round(9.5));//四舍五入
System.out.println(Math.random());//返回[0,1)之间的随机数
public class RandomDemo {
static Random random=new Random();
public static void main(String[] args) {
System.out.println(random.nextBoolean());//布尔值随机数
System.out.println(random.nextInt());//int型随机数
System.out.println(random.nextLong());//long型随机数
System.out.println(random.nextInt(5));//给定范围,[0,范围)随机整数
}
}
十四、集合
(一)解释
在Java中,集合和容器是非常重要的概念,用于存储和操作数据。在集合中,有单列集合和双列集合两种类型。
单列集合:
Collection接口:定义了单列集合共有的方法
List:可以有重复的元素
ArrayList:基于数组实现的列表,可以动态扩展数组的长度
LinkedList:基于链表实现的列表
Vector:类似ArrayList,但是线程安全
Set:不允许有重复元素的集合
双列集合:
键值对的集合,将键和值进行关联存储,形成一段数据
有人就要说了,既然集合与容器都是用于存储和操作数据,我们已经有了数组为何还要费尽心思设计集合这个概念呢
数组的特点总结如下:
一组数据类型相同的元素集合
创建数组时必须给定长度,且长度一旦创建后不能改变
当数组装满时,需要创建一个新的数组并将元素搬迁过去
不方便进行动态操作:
判断是否装满需要额外逻辑
如果数组中间删除或添加元素,需要移动后面的元素
这些限制和不便之处使得数组在某些场景下不够灵活,因此Java提供了更多种类的集合类来解决这些问题,使数据存储更加方便和高效。
(二)ArrayList类
1、构造方法:
ArrayList arrayList =new ArrayList();
2、相关方法及解释
(1)add(element);先检测容量,不够则扩容至1.5倍(数组复制)
add(index,element);
(2)remove();删除并返回
(3)indexOf(element);从后查找并返回索引
(4)lastIndexOf(element);从后查找并返回索引
(5)set(index,element);给出索引,按照索引替换,并返回被替换的元素
(6)clear();清空集合
(7)isEmpty();判断集合是否为空
(8)constains(element);判断是否包含某一元素
(9)size();返回集合中元素的个数
(10)get(index);获取索引位置的元素
(三)LinkList类
1、构造方法
LinkedList<数据类型> 对象名=new LinkedList<>();
2、相关方法及其解释
(1)add();默认添加到链表末尾
(2)add(index,element);指定位置添加元素
(3)get(index);获取索引位置上的元素
(4)indexOf(element);从前查找获取索引
(5)lastIndexOf(element);
(6)remove(element);删除指定元素
remove(index);删除索引位置的元素
(7)removeFirst();删除首元素
(8)removeLast删除末尾元素
(9)addFirst();在前插入
(10)addLast();在末尾插入
(11)pop();出栈
(四)Iterator迭代器接口
1)构造对象
Iterator 迭代器对象名=迭代器想要遍历的对象.iterator();
(2)获取下一个元素
.next()
(3)删除
.remove() Iterator只能单向遍历
(五)、LastIterator迭代器接口
1、构造方法
ListIterator<数据类型> 迭代器对象名=迭代器想要遍历的对象.listIterator();//默认从0开始
2、从前往后遍历
while(iterator2.hasNext()){
System.out.println(iterator2.next());
}
3、从后向前遍历
while (iterator1.hasPrevious()){//从后想前遍历
System.out.println(iterator1.previous());
}
4、从指定位置遍历
ListIterator<String> iterator3=arrayList.listIterator(1);//指定从1开始遍历
while (iterator3.hasPrevious()){//从后想前遍历
System.out.println(iterator3.previous());
}
(六)HashSet
hashSet是set集合的子类之一,具有如下特点
不允许重复元素:HashSet中不允许存储重复元素,每个元素在HashSet中只会出现一次。当尝试向HashSet中添加重复元素时,新元素不会被添加进集合。
无序性:HashSet中的元素没有固定的顺序,即不保证元素的顺序与插入顺序相同。这是因为HashSet内部使用哈希表来存储元素,元素的存储位置是根据元素的哈希码决定的。
高效的查找操作:由于HashSet基于哈希表实现,查找元素的效率很高。当需要判断某个元素是否存在于HashSet中时,HashSet会通过计算元素的哈希码来快速找到对应的存储位置,从而实现快速的查找操作。
添加、删除元素效率高:HashSet对于添加和删除元素的操作效率也很高,因为它们都可以通过哈希码快速定位到元素的存储位置。
hashSet主要的方法如下(成员方法,需要定义hashSet对象)
set.clear();//清空
set.contains();//判断是否包含某一元素
set.remove();//删除
set.iterator();//获取迭代器对象
hashSet的主要遍历方式
1、使用迭代器(Iterator)进行遍历:
Set<Object> hashSet = new HashSet<>();
// 添加元素到HashSet
hashSet.add("Apple");
hashSet.add("Banana");
hashSet.add("Orange");
Iterator<Object> iterator = hashSet.iterator();
while (iterator.hasNext()) {
Object element = iterator.next();
// 处理每个元素
System.out.println(element);
}
2、使用增强型for循环进行遍历
Set<Object> hashSet = new HashSet<>();
// 添加元素到HashSet
hashSet.add("Apple");
hashSet.add("Banana");
hashSet.add("Orange");
for (Object element : hashSet) {
// 处理每个元素
System.out.println(element);
}
当set集合添加元素时如何判断元素重复的?
如果每次都使用equals效率很低,肯定不是hashSet底层所使用的。其实在底层使用的是hashCode();
Object的hashCode的方法是判断地址,而Set集合调用的是重写的hashCode方法,根据内容计算哈希值;遍历时会先使用哈希值比较是否相等,再使用equals,这既保证效率又保证安全。我们使用的基本数据类型包装类中都已经将hashCode()方法重写过,可以放心使用。但是当我们自定义一个数据类型时,就需要重写hashCode()方法,以确保HashSet集合可以很好的比较和排序
例如我们写一个学生类型,包括成员变量姓名、年龄;可以这样写
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
(七)TreeSet
TreeSet是Set接口的另一个常见实现类,它基于红黑树(Red-Black Tree)实现,具有以下特点:
自动排序:TreeSet中的元素会按照它们的自然顺序(natural ordering)或者通过Comparator接口指定的顺序进行排序。当元素被添加到TreeSet中时,它们会被自动排序,这使得TreeSet是一个有序的集合。
不允许重复元素:与HashSet类似,TreeSet中不允许存储重复元素,每个元素在TreeSet中只会出现一次。
高效的查找操作:由于TreeSet内部基于红黑树实现,查找、插入、删除元素的操作效率都很高。红黑树是一种自平衡的二叉搜索树,保证了元素的快速查找和操作。
提供有序性:TreeSet中的元素是有序的,可以根据元素的自然顺序或者指定的排序规则进行遍历。这使得TreeSet适合需要有序集合的场景。
TreeSet<Integer> set =new TreeSet<>();
set.add(3);
set.add(4);
set.add(1);
set.add(2);
set.add(3);
System.out.println(set);//[1, 2, 3, 4]
set.clear();//清空
set.size();//长度
set.contains("");//是否包含某一元素
set.first();//删除并返回第一个元素
set.isEmpty();//判断是否为空
set.remove(1);//删除指定元素
set.pollLast();//删除并返回最后一个元素
需要注意的是:TreeSet中的元素必须实现Comparable接口并且重写compareTo()方法,用于确定排序的规则
案例
public class Student implements Comparable<Student> {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
return this.age-o.age;
}
}
(八)hashMap--基于哈希表实现,可以提供快速的插入、查找和删除操作。
常用方法:
public static void main(String[] args) {
HashMap<String,String> map =new HashMap<>();
map.put("c","cc");//添加
map.put("x","xx");
map.put("z","zz");
map.put("a","aa");
map.put("c","CC");
map.put("h","hh");
System.out.println(map);
map.get("c");//输入key获取value
map.clear();//清空
map.remove("h");//输入key删除这个键值对并返回value
map.containsKey("k");//判断是否包含该键,返回布尔值
map.containsValue("aa");//判断是否包含这个值,返回布尔值
map.isEmpty();//判断是否为空返回布尔值
map.size();//返回键值对数量
map.values();//将所有的值存入单列集合 Collection类型对象接收
map.keySet();//将所有的key存入set集合 set类型对象接收
}
遍历
public static void main(String[] args) {
HashMap<String,String> map =new HashMap<>();
map.put("c","cc");//添加
map.put("x","xx");
map.put("z","zz");
map.put("a","aa");
map.put("c","CC");
map.put("h","hh");
//方式1 同过map.set将所有key存入集合,通过map.get获取value
Set<String> keySet =map.keySet();
for(String key:keySet){
System.out.println(key+":"+map.get(key));
}
System.out.println();
//方式2:map.entrySet();返回一个Entry类的集合,Entry里包含键值对
//Entry包含相关方法
Set<Map.Entry<String,String>>entries=map.entrySet();
for(Map.Entry<String,String> entry:entries){
System.out.println(entry.getKey()+entry.getValue());
}
}
put方法的底层逻辑
(九)TreeMap--TreeMap
是Java中另一个常用的双列集合,它基于红黑树实现,可以提供按照键的顺序进行遍历的特性。
package com.wbc.javaclllection.Map双列集合.TreeMap;
import java.util.TreeMap;
public class TreeMapDemo {
/*
map: 键值对 键不能重复 值可以重复
TreeMap:键可以排序 底层使用树结构 键的类型必须实现Comparable接口
*/
public static void main(String[] args) {
TreeMap<Integer,String> treeMap =new TreeMap<>();
treeMap.put(1,"a");
treeMap.put(2,"c");
treeMap.put(5,"d");
treeMap.put(4,"e");
treeMap.put(3,"a");
System.out.println(treeMap);
//方法与HashMap一致
}
}
(十)HashTable--类似于HashMap
,也是基于哈希表实现的。
HashTable底层与HashMap相同,但是HashTable是线程安全的 方法多了一个synchronized关键字 HashMap可以有一个键为null、值也可以为null,HashTable不能存储null,键值都不行
(十一)集合思维导图
十五、IO流
(一)File类
一个File类的对象,可以表示计算机硬盘上一个具体的文件或目录(文件夹),通过File类的对象,来获取文件/目录的相关信息,但不能读取文件中的内容。
常用方法
package com.wbc.IO.File;
import java.io.File;
import java.util.Date;
public class FileDemo {
/*
一个File类的对象,表示计算机硬盘上一个具体的文件或目录(文件夹)
通过File类的对象,来获取文件或目录的相关信息,例如创建时间,是否可写....但是不可以读取文件中内容的
*/
public static void main(String[] args) {
File f =new File("C:\\Users\\17903\\Desktop\\a.txt");//有后缀为文件
File f1 =new File("C:\\Users\\17903\\Desktop\\a");//无后缀为文件夹
System.out.println(f.canWrite());//返回布尔值文件是否可以写入
System.out.println(f.canRead());//返回布尔值文件是否可读
System.out.println(f1.exists());//判断文件或目录是否存在
System.out.println(f.getAbsoluteFile());//获取绝对地址
System.out.println(f.isHidden());//判断是否是隐藏文件
System.out.println(f.isDirectory());//判断是否是目录
System.out.println(f.isFile());//判断是否是文件
System.out.println(new Date(f.lastModified()));//lastModified返回最后修改时间,long类型,需要new一个Date类
System.out.println(f.getName());//获取文件名
System.out.println(f.list());//获取下一层子集文件名,返回String类型数组
System.out.println(f.listFiles());//返回File类型数组
System.out.println(/*f.createNewFile()*/);//创建新文件
System.out.println(f.mkdir());//创建单层目录
System.out.println(f.mkdirs());//创建多层目录
System.out.println(f.length());//文件内容的长度(字节长度)
}
}
创建删除
package com.wbc.IO.File;
import java.io.File;
import java.io.IOException;
public class FileMethod {
/*
File类的方法
*/
public static void main(String[] args) throws IOException {
//1、创建文件
File f =new File("C:\\Users\\17903\\Desktop\\ab.txt");
if(!f.exists()){//如果文件不存在
boolean res=f.createNewFile();//创建新的文件(只能创建文件,不能创建目录)
//当new的时候位置不存在,可能会报错(系统找不到指定的路径)
System.out.println(res);
}
//2、删除文件
boolean res1=f.delete();
System.out.println(res1);
//3、创建文件夹
File f1 =new File("C:\\Users\\17903\\Desktop\\ab");
//创建单层目录
boolean res2=f1.mkdir();
System.out.println(res2);
//创建多层目录
File f2 =new File("C:/aa/bb/cc");
boolean res3 =f2.mkdirs();
System.out.println(res3);
//4、删除文件夹
//删除单层文件夹
System.out.println(f1.delete());//delete可以删除文件夹,但只能删除空的文件夹,并且删除后不进回收站
//删除多层文件夹
//获取文件夹中子集的名字
File f3 =new File("C:/aa");
String[] strings = f3.list();//只能获取当前文件夹的下一级所有文件的名字,下下一级无法获取,在删除时非常麻烦
for(String s:strings){
System.out.println(s);
}
//获取文件夹中的子集,将其定义成File类对象
File[] files=f3.listFiles();
for(File file:files){
file.delete();//只能删除a目录里的文件,而文件夹无法删除
}
//递归删除
/*
public static void deleteDirectory(File directory) {
if (directory.isDirectory()) {
File[] files = directory.listFiles();
if (files != null) {
for (File file : files) {
deleteDirectory(file);
}
}
}
directory.delete();
}
*/
}
}
(二)、IO流的相关概念
字节:计算机最小存储单位是字节,电脑上所有的文件最终都是以字节的形式存储到硬盘的,例如:图片,视频,音频……
字符:文字,其底层还是字节,例如:中--》20013->三个字节
字符流是一字符为单位进行读写操作,底层将原始的字节转为字符,字符流只能读取纯文本文件(只能读取文字)
字节流:一次读写操作以字节为单位(类为Stream结尾)
节点字节输入流 :文件输入字节流InputStream
处理字节输入流 :缓冲输入字节流BufferedInputStream
节点字节输出流 :文件输出字节流OutputStream
处理字节输出流 :缓冲输出字节流BufferedOutputStream
字符流:一次读写操作以字符为单位
字符输入流Reader
文件字符转换流InputStreamReader
文件字符输入流:FileReader
字符输出流Writer
文件字符转换流OutputStreamReader
文件字符输出流:FileWriter
打印输出字符流:PrintWriter
节点流:原始的流,直接用来操作文件(数据)例如:FileInputStream
处理流:在节点流的基础上,对读到的数据进行二次处理
(三)、字节流
(1)节点流--FileInputStream、FileOutputStream
package com.wbc.IO.输入输出流.字节流.节点流.File;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileStreamMethods {
/*
in.read();默认只读取一个字节,返回的是字节编码,效率低下
in.read(byte[] b);默认一次读一个指定大小的byte数组个字节,返回的是数组中一次实际装入的字节个数,读完后返回-1
out.write();默认只写入一个字节
out.write(byte[],int off,int len);将数组中存入的字节写入文件 用法:out.write(数组,开始位置,起始位置)
.close 关闭流对文件的控制
*/
public static void main(String[] args) throws IOException {
File f =new File("C:\\Users\\17903\\Desktop\\a.txt");
if(!f.exists()){
f.createNewFile();
}
FileInputStream inputStream = new FileInputStream(f);
File f1 =new File("C:\\Users\\17903\\Desktop\\b.txt");
if(!f1.exists()){
f1.createNewFile();
}
FileOutputStream outputStream=new FileOutputStream(f1);
//直接传输,效率满
int t=0;
while((t=inputStream.read()) != -1){//读入
System.out.println(t);
outputStream.write(t);//写入
}
//打包传输,效率块
byte[] bytes =new byte[100];
int size=0;
while((size=inputStream.read(bytes))!=-1){
outputStream.write(bytes,0,size);//read返回实际装入的字节个数,用size来控制从0写到byte数组实际存入的字节的末尾
}
inputStream.close();
outputStream.close();
}
}
(2)处理流
1)缓冲输入输出流--BufferedInputStream、BufferedOutputStream
package com.wbc.IO.输入输出流.字节流.处理流;
import java.io.*;
public class BufferedStreamDemo {
public static void main(String[] args) throws IOException {
//创建一个缓冲输入流对象
File f =new File("C:/Users/17903/Desktop/ab.png");
if(!f.exists()){
f.createNewFile();
}
FileInputStream inputStream = new FileInputStream(f);//节点流
/*
底层有一个容量为8192字节的byte数组,节点流读出的字节存入该数组
*/
BufferedInputStream bufferedInputStream =new BufferedInputStream(inputStream);//多态 传入节点流,负责缓冲处理
File f1 =new File("C:/Users/17903/Desktop/ba.png");
if(!f1.exists()){
f1.createNewFile();
}
FileOutputStream outputStream=new FileOutputStream(f1);
/*
构造时可以自定义底层缓冲区的大小
BufferedOutputStream(OutputStream out, int size)
*/
BufferedOutputStream bufferedOutputStream=new BufferedOutputStream(outputStream);
byte[] bytes =new byte[1024];
int size = 0;
while ((size=bufferedInputStream.read(bytes))!=-1){
bufferedOutputStream.write(bytes,0,size);
}
bufferedInputStream.close();
bufferedOutputStream.flush();//刷新缓冲区
bufferedOutputStream.close();
}
}
2)数据输入输出流--DataIntputStream、DataOutputStream
package com.wbc.IO.输入输出流.字节流.处理流;
import java.io.*;
import java.util.Scanner;
public class DataStreamDemo {
static Scanner scanner =new Scanner(System.in);
public static void main(String[] args) throws IOException {
/*
网络传输字符时如果使用FileStream十分麻烦
String s="你好";
FileOutputStream out =new FileOutputStream("");
out.write(s.getBytes());//字符串转为byte数组
FileInputStream in =new FileInputStream("");
byte[] bytes =new byte[100];
int size =in.read(bytes);//接收到之后也是byte数组
String str =new String(bytes,0,size);
System.out.println(str);
out.close();
in.close();
可以使用DataStream
*/
String s=scanner.next();
File f =new File("C:\\Users\\17903\\Desktop\\c.txt");
if(!f.exists()){
f.createNewFile();
}
FileOutputStream outputStream=new FileOutputStream(f);
DataOutputStream dataOutputStream=new DataOutputStream(outputStream);
dataOutputStream.writeUTF(s);
FileInputStream inputStream = new FileInputStream(f);
DataInputStream dataInputStream =new DataInputStream(inputStream);
String s1=dataInputStream.readUTF();
System.out.println(s1);
}
}
3)对象输入输出流--ObjectInputStream、ObjectOutputStream
package com.wbc.IO.输入输出流.字节流.处理流.对象输入输出流;
import java.io.*;
import java.util.Date;
public class ObjectStreamDemo {
/*
将对象信息写入到文件内使其完整保存下来 ,以免文件结束后对象信息消失
将对象写入文件的过程叫做对象序列化
将文件中信息读入到文件的过程叫做对象反序列化
返回的过程会在程序中创建一个新对象,这也是java中创建对象的另一种方法
*/
public static void main(String[] args) throws IOException, ClassNotFoundException {
//写入
String s =new String("Abv");
Date date = new Date();
FileOutputStream outputStream =new FileOutputStream("D:/12.txt");
ObjectOutputStream objectOutputStream =new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(s);
objectOutputStream.writeObject(date);
objectOutputStream.close();
//读取
FileInputStream inputStream =new FileInputStream("D:/12.txt");
ObjectInputStream objectInputStream =new ObjectInputStream(inputStream);
String s1=(String) objectInputStream.readObject();
Date date1=(Date)objectInputStream.readObject();
System.out.println(s1);//Abv
System.out.println(date1);//Sat Mar 23 09:32:58 CST 2024
objectInputStream.close();
}
}
4)自定义对象输入输出流
自定义对象
package com.wbc.IO.输入输出流.字节流.处理流.对象输入输出流.自创对象输入输出;
import java.io.Serializable;
/*
一旦一个类实现了Serializable接口,会自动实现一个序列化编号(唯一)
*/
public class User implements Serializable {
private static final long serialVersionUID = -5941757266582122064L;//在类中定义一个final的序列编号,当类信息修改,序列号不变
private String name;
private String passNum;
//当添加了transient关键字的属性,在序列化时不会被保存到文件中
private transient String data;
public User(String name, String passNum) {
this.name = name;
this.passNum = passNum;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", passNum='" + passNum + '\'' +
'}';
}
//当对象进行修改时,一旦信息进行了修改,序列化编号会发生改变
//导致序列号发生变化产生报错
//解决方法:在类中定义一个final的序列编号
}
对象输入输出流
package com.wbc.IO.输入输出流.字节流.处理流.对象输入输出流.自创对象输入输出;
import java.io.*;
public class ObjectStreamTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//在使用对象输入输出流时,必须先new Output再new Input
FileOutputStream fileOutputStream =new FileOutputStream("D:/123456.txt");
ObjectOutputStream objectOutputStream=new ObjectOutputStream(fileOutputStream);
User user =new User("abc","1232456");
//当一个类的对象用对象输入输出流写入或输出时,需要实现序列化接口
objectOutputStream.writeObject(user);
//Exception in thread "main" java.io.NotSerializableException
//提示自创对象没有序列化编码
FileInputStream fileInputStream =new FileInputStream("D:/123456.txt");
ObjectInputStream objectInputStream=new ObjectInputStream(fileInputStream);
User user1 =(User)objectInputStream.readObject();
System.out.println(user1);
}
}
(四)、字符流
(1)节点流--FileReader、FIleWriter
package com.wbc.IO.输入输出流.字符流.节点流;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class FileReaderWriterDemo {
/*
writer.write方法:void write(char cbuf[], int off, int len) throws IOException
reader.read方法:int read(char cbuf[]) throws IOException
*/
public static void main(String[] args) throws IOException {
File f =new File("C:\\Users\\17903\\Desktop\\a.txt");
if(!f.exists()){
f.createNewFile();
}
FileReader reader = new FileReader(f);
File f1 =new File("C:\\Users\\17903\\Desktop\\b.txt");
if(!f1.exists()){
f1.createNewFile();
}
FileWriter writer = new FileWriter(f1);
char[] chars =new char[100];
int size=0;
while((size=reader.read(chars))!=-1){
writer.write(chars,0,size);
}
reader.close();
writer.flush();
writer.close();
}
}
(2)处理流
1)缓冲输入输出流--BufferedWriter、BufferedReader
package com.wbc.IO.输入输出流.字符流.处理流;
import java.io.*;
public class BufferedReaderWriter {
public static void main(String[] args) throws IOException {
File f =new File("C:\\Users\\17903\\Desktop\\a.txt");
if(!f.exists()){
f.createNewFile();
}
FileReader reader = new FileReader(f);
BufferedReader bufferedReader =new BufferedReader(reader);
File f1 =new File("C:\\Users\\17903\\Desktop\\b.txt");
if(!f1.exists()){
f1.createNewFile();
}
//true表示可以追加
// FileWriter(File file, boolean append) throws IOException
FileWriter writer = new FileWriter(f1,true);
BufferedWriter bufferedWriter =new BufferedWriter(writer);
/* //遍历字符添加
char[] chars =new char[100];
int size=0;
while((size=bufferedReader.read(chars))!=-1){
bufferedWriter.write(chars,0,size);
}*/
//遍历行添加
String line =null;
while((line=bufferedReader.readLine())!=null){
System.out.println(line);
bufferedWriter.write(line,0,line.length());
bufferedWriter.newLine();//换行符
}
bufferedReader.close();
bufferedWriter.flush();
bufferedWriter.close();
}
}
2)打印输出处理流--printWriter
package com.wbc.IO.输入输出流.字符流.处理流.打印输出处理流;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
public class PrintWriterDemo {
public static void main(String[] args) throws FileNotFoundException {
/*
PrintWriter
单项输出,
后面再javaEE(服务器开发)中,可以使用PrintWriter从后端程序向前端程序相应数据
*/
PrintWriter printWriter =new PrintWriter("C:\\Users\\17903\\Desktop\\index.html");
printWriter.write("<h1>你好</h1>");
printWriter.write("<h2>你好</h2>");
printWriter.write("<h3>你好</h3>");
printWriter.close();
}
}
十六、异常
(一)、异常概述
什么是异常:
程序运行中出现的不正常情况
如:数组越界 int[] a=new int[5];
b=a[5];//访问时数组越界
程序运行过程中由于用户输入的格式有问题、读取文件文件被删除了、网络传输过程中断
网了等就会导致程序出现不正常情况-->异常
需要注意的是,语法错误并非异常
java中的默认异常处理机制:
当程序出现异常后,会将异常的信息包装在一个对应的对象中,并抛出此对象并终止程序运行
控制台报错时,结构是 在某某线程中,异常的类型,异常的原因,异常的位置
如:Exception in thread "main" //主线程
java.lang.ArrayIndexOutOfBoundsException: 5//类型是数组越界,原因是索引值5
at com.wbc.Exception.异常概述与常用异常.ExceptionDemo.main(Demo1.java:9)
//程序运行的位置
异常的处理:
1、遇到异常就终止程序运行
2、编写代码时就充分考虑可能出现的情况。实在无法避免的要编写代码进行处理
(二)、常见的异常
//ArrayIndexOutOfBoundsException数组越界
int[] a =new int[5];
/*for(int i =0;i<=5;i++){
a[i]=i;
}
//java.lang.StringIndexOutOfBoundsException字符串越界
String s ="abcd";
s.charAt(4);
//java.lang.ArithmeticException算数异常
/*int x=10;
int y=0;
int z=x/y;*/
//java.lang.NullPointerException空指针
/*String s=null;
s.length();*/
//java.lang.ClassCastException类型转换异常
/*Object s ="abcd";
Integer ac =(Integer)s;*/
//java.lang.NumberFormatException数字格式化异常
/*int ab = Integer.parseInt("abc0");
System.out.println(a);*/、
(三)、异常的体系结构
异常的体系结构
Java.lang.Throwable 异常体系的根父类
内韩两个常用方法:
public void printStacktrace();打印栈中的异常信息
public String getMessage();获取异常的信息
Throwable的两个子类
Error:错误
java虚拟机无法解决的严重问题,如:jvm系统内部错误,资源耗尽、内存不够用等
是系统姓错误,java程序无法解决
Exception:异常(这是本章要学习的)
是出现的一般性问题,可以使用java中的异常处理机制进行处理的
运行时:ArithmeticException、NullPointerException...
非运行时:
广义异常:所有的报错都可以称为异常
狭义异常:Exception
(四)、异常的处理
异常处理
java中提供一套异常处理机制没在程序发生异常时,可以执行预先设定好的处理程序,执行完成
后,程序会停止,可以继续向后执行
在写代码时,就要根据不同的情况设定好处理程序,
运行程序:
程序执行时,出现问题,执行异常处理程序
没有出现问题,则不需要进行异常处理程序
关键字:
try{
执行可以产生异常的代码
}
catch(){
处理方式
}
finally{
必须要执行的代码
}
thorw:用于手动抛出异常,一般写在方法体之内,当满足或不满足某种情况需要抛出时使用
throws:抛出异常,一般写在方法声明中,当运用此方法时可获得其提示的异常类型,方便处理
相关示例
1)try-catch-finally
package com.wbc.Exception.异常处理.catch_finally;
public class Demo2 {
public static void main(String[] args) {
try {
String s =null;
s.length();
int a = 10;
int b = 0;
int c = a / b;
}
catch (ArithmeticException a){
a.printStackTrace();
System.out.println("除数不能为0");
}
catch (NullPointerException n){
//try中的代码一旦出现异常,后面的代码就不会执行了转而执行对应的catch
n.printStackTrace();
System.out.println("对象为空指针");
}
catch (Exception e){//写Exception是在怕分辨不出哪里异常,但zx需要写到最底下
System.out.println("程序异常"+e.getMessage());
}
finally {
//必须要执行的代码
//例如IO里的close
}
System.out.println("over");
}
}
2)throws
package com.wbc.Exception.异常处理.throws_throw;
import java.io.UnsupportedEncodingException;
public class demo4 {
public static void main(String[] args) {
//不强制处理是因为这是个运行时异常,在运行时才可能会异常抛出
//在编译期间不会强制处理,在写代码时需要注意
chu(1,0);
//这是个编译器异常,在编译的时候就会异常抛出
//处理方法有两种 1、try_catch 2、throws 一般到顶层就不能再throws了
try {
test();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
/*
在方法声明的地方,通过throws关键字,声明此方法可能会出现异常
使用throws抛出异常表示此方法不处理异常,交给调用这个方法的地方进行处理
一般在底层的方法中都不进行处理
*/
public static int chu(int a,int b)throws ArithmeticException{
int c =a/b;
return c;
}
public static void test() throws UnsupportedEncodingException {
"abc".getBytes("utf-8");
}
}
3)throw
package com.wbc.Exception.异常处理.throws_throw;
import java.io.UnsupportedEncodingException;
public class demo4 {
public static void main(String[] args) {
//不强制处理是因为这是个运行时异常,在运行时才可能会异常抛出
//在编译期间不会强制处理,在写代码时需要注意
chu(1,0);
//这是个编译器异常,在编译的时候就会异常抛出
//处理方法有两种 1、try_catch 2、throws 一般到顶层就不能再throws了
try {
test();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
/*
在方法声明的地方,通过throws关键字,声明此方法可能会出现异常
使用throws抛出异常表示此方法不处理异常,交给调用这个方法的地方进行处理
一般在底层的方法中都不进行处理
*/
public static int chu(int a,int b)throws ArithmeticException{
int c =a/b;
return c;
}
public static void test() throws UnsupportedEncodingException {
"abc".getBytes("utf-8");
}
}
4)自定义异常
java API中定义的时标准异常类,都是与语法相关的,如索引越界、空指针等 在编写程序时,有可能在满足某种业务需求时,想要以抛出异常的方式进行处理 就需要自定义一个与业务相关的异常类来表示(如同分数不合法--ScoreException)
package com.wbc.Exception.异常处理.自定义异常;
public class ScoreException extends Exception{
public ScoreException() {
}
public ScoreException(String message) {
super(message);//调用父类的方法
}
}
package com.wbc.Exception.异常处理.自定义异常;
public class Score {
public static void main(String[] args) {
int score =110;
try {
checkScore(score);
} catch (ScoreException e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
}
public static char checkScore(int score) throws ScoreException {
if(score<0||score>100){
//一般在方法体内
// 当满足或不满足某种条件时,主动抛出一个异常对象,程序不可继续执行
throw new ScoreException("分数非法");
}
else if(score<60){
return 'D';
}
else if(score>=60&&score<75){
return 'C';
}
else if(score>=75&&score<85){
return 'B';
}
else
return 'A';
}
}
十七、网络编程
(一)、网络编程概述
什么是计算机网络
把不同区域的计算机(广义)通过通信设备和线路连接,可以实现数据的传输和共享的系统。
实现不同计算机之间的练习,必须有介质连接。
网络编程是干什么的
聊天-->聊天软件 QQ
java语言是支持网络间的数据传输的,将底层的细节都封装起来了,给程序员提供了一套标准的类库,可以很方便的使用java语言开发出进行网络通信的软件
网络编程:
借助计算机网络实现我们所写的程序可以在不同的电脑上进行数据传输
网络编程的核心问题
如何找到网络世界上的目标主机,以及目标软件
win+r -->cmd-->ipconfig 查看自己电脑的ip
如何安全可靠的进行数据传输-->协议 规则
网络模型
OSI参考模型:理性化标准模型
分成七层
TCP/IP参考模型
实际使用的参考模型
实际分为四层:
应用层(http)
⬇
运输层(协议) ^逐层解析
⬇ |
网络层(IP) |
⬇ |
物理层(硬件、网卡)-->物理层
通信要素:ip 端口 协议
ip:
IP地址是指互联网协议地址,又译为网际协议地址
网络中的计算机使用IP地址来进行唯一标识.
在Windows系统下,打开cmd,输入命令ipconfig,按回车即可查看(局域网)
连接到服务器,会自动分配ip
本地回环地址(hostAddress):127.0.0.1 ,它代表设备的本地虚拟接口
端口:
0--1024是被系统使用或保留的端口号,0——65535是有效端口号
我们在定义的时候需要定义1025——65535之间的端口,端口号不可与已有端口冲突
协议(规则、规范、约定):
规定传输速率,代码结构,出错后如何应对等等规则
传输层的重要协议:
传输控制协议TCP:
客户端向服务器发送数据前,首先要建立连接(测试网络是否通畅)
正是传输数据
断开前还要相互确认
可靠安全但效率相对低
TCP三次握手: 先向服务器发送请求
服务器收到请求后会给客户端回应
客户段为服务器的回应做确认的回应,告知服务器客户端收到了服务器的回复开始传输数据
TCP四次挥手:
客户端向服务器发送断开请求
服务器向客户端做出回应
服务器把没传完的数据传输完毕,再向客户端做出回应
客户端向服务器的回应做出回应
用户数据报协议UDP:
报要发送的数据封装成数据报(数据包)
数据报包含数据,对方IP,对方端口,只管发送即可,是否发送成功不明确
效率相对高但不可靠,可能会丢数据
(二)、Tcp传输协议实现
服务端
package com.wbc.Internet.homework.TCP;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Severs {
static Scanner scanner =new Scanner(System.in);
public static void main(String[] args) {
try {
ServerSocket serverSocket =new ServerSocket(1234);//端口号1234
System.out.println("服务器创建成功");
Socket socket =serverSocket.accept();
System.out.println("客户端连接成功");
while(true){
java.io.DataInputStream inputStream =new DataInputStream(socket.getInputStream());
System.out.println("对方正在输入中……");
String s =inputStream.readUTF();
System.out.println("客户端:"+s);
DataOutputStream outputStream =new DataOutputStream(socket.getOutputStream());
String s1 =scanner.next();
System.out.println("待输入……");
outputStream.writeUTF(s1);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端
package com.wbc.Internet.homework.TCP;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;
public class Clients {
static Scanner scanner =new Scanner(System.in);
public static void main(String[] args) {
try {
Socket socket =new Socket("127.0.0.1",1234);//本地回环测试
System.out.println("客户端连接成功");
while(true){
DataOutputStream outputStream =new DataOutputStream(socket.getOutputStream());
System.out.println("待输入……");
String s =scanner.next();
outputStream.writeUTF(s);
DataInputStream inputStream =new DataInputStream(socket.getInputStream());
System.out.println("对方正在输入……");
String s1=inputStream.readUTF();
System.out.println("服务器:"+s1);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
十八、线程
(一)、线程的概念
(1)程序,进程和线程
程序pragam: 为解决某种问题,使用计算机语言编写的一系列指令(代码)的集合。本章中的程序,特指的是静态的,安装在硬盘上代码集合。
进程: 运行中的程序(被加载到内存中), 是操作系统进行资源分配的最小单位
线程: 进程可以进一步细化为线程,是进程内一个最小执行单元(具体要做的事情),是cpu进行任务调度的最小单位.
运行中的QQ 就是一个进程, 操作系统会为这个进程分配内存资源,一个聊天窗口就认为是一个线程, 这多个聊天窗口可以同时被cpu执行,但是这些聊天窗口属于进程, 线程是属于进程的.
早期没有线程,早期cpu执行的时候,是以进程为单位执行,进程单位还是比较大的,当一个进程运行时,其他的进行就不能执行,所以后来,将进程中的多个任务,细化为线程,cpu执行单位,也从进程转为更小的线程.
(2)进程和线程的关系
一个进程中可以包含多个线程
一个线程只能隶属于一个进程,线程不能脱离进程存在
一个进程中至少有一个线程(即主线程) java中的main方法,就是用来启动主线程
在主线程中可以创建并启动其他线程
所有线程都共享进程内存资源.
(二)、线程的创建与常用方法
1)创建线程
创建线程共有三种方式
1、继承Thread类
写一个线程类,通过继承Thread类并重写run方法,并在程序中创建类并调用的方法进行使用
例:
myThread类
public class MyThread extends Thread{
/*
java中创建线程的方法
写一个类继承java.lang.Thread
重写run()
*/
@Override
public void run() {
/*
线程中要执行的任务都要写到run方法中,或者在run方法中进行调用
*/
for(int i=0;i<1000;i++){
System.out.println("MyThread"+i);
}
}
}
测试类:
import com.wbc.Thread.多线程.方式1继承Thread.MyThread;
public class threadTest {
public static void main(String[] args) {
//创建并启动线程
MyThread myThread =new MyThread();
//myThread.run();这不是启动线程,只是一个方法调用,本质仍是单线程
myThread.start();
for(int i=0;i<1000;i++){
System.out.println("main:"+i);
}
/*
MyThread941
MyThread942
MyThread943
main:957
main:958
main:959
main:960
MyThread944
MyThread945
MyThread946
MyThread947
*/
}
}
在需要开启线程的地方new继承了 Thread类的线程类
需要注意的是,使用 线程需要调用的是.start()方法而不是run方法,单纯调用run方法本质还是单线程调用
2、实现Runnable接口
创建一个任务类以实现Runnable接口,通过new Thread(任务类).start启动线程
例:
任务类:
public class Task implements Runnable{
/*
java中创建线程方式2
创建一个类实现Runnable接口
重写run方法
*/
@Override
public void run() {
for (int i=0;i<10000;i++){
System.out.println("task:"+i);
}
}
}
测试类:
package com.wbc.Thread.多线程.方式2实现接口;
public class TaskTest {
public static void main(String[] args) {
//创建任务
Task task =new Task();
//创建线程
Thread thread =new Thread(task);
thread.start();
for(int i=0;i<10000;i++){
System.out.println("main:"+i);
}
/*
main:9448
main:9449
main:9450
task:8954
task:8955
task:8956
task:8957
task:8958
task:8959
task:8960
main:9451
main:9452
*/
}
}
测试发现主线程(main)中for 循环打印的i和线程打印交替进行,证明是双线程
3、实现Callable接口
创建一个任务类以实现Callable接口并重写call方法
FutureTask futureTask =new FutureTask(任务类),创建将来执行任务的对象
new Thread(futureTask).start();
例:
任务类
import java.util.concurrent.Callable;
public class SumTask<T> implements Callable<T> {
@Override
public T call() throws Exception {
Integer i =0;
for(int j=0;j<100;j++){
i+=j;
}
return (T)i;
}
}
测试类:
package com.wbc.Thread.多线程.方式3实现Callable接口;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Test {
public static void main(String[] args) {
SumTask<Integer> sumTask =new SumTask<>();
FutureTask futureTask =new FutureTask(sumTask);
Thread thread =new Thread(futureTask);
thread.start();
try {
Integer i =(Integer) futureTask.get();//获取返回值
System.out.println(i);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
推荐使用实现Runnable接口进行创建线程
由于java是单继承的,继承了Thread类,就不能继承其他类了。实现Runnable接口避免了单继承的局限性 并且实现Runnable接口进行创建线程更适合多线程共享同一份资源的场景
当需要使用泛型时可以使用Callable接口
2)线程的相关方法
Thread表示线程,提供了很多的方法,来对线程程进行控制.
run(); 线程要执行的任务
start(); 启动java线程的
构造方法
new Thread(Runable runnablee); 接收一个任务对象..
new Thread(Runable runnablee,String name); 接收一个任务对象,并为线程设置名字
setName("我的线程1"); 为线程设置名字
String getName(); 获得线程的名字
Thread..currentThread();在任务中获得当前正在执行的线程
setPriority(int p11)设置优先级 1-10之间 默认是5
getPriority(); 获得优先级
sleep(long mm); 让线程休眠指定的时间 毫秒单位
join(); 让其他线程等待当前线程结束
myThread类
package com.wbc.Thread.多线程.ThreadMethods;
public class ThreadTest {
public static void main(String[] args) throws InterruptedException {
/*
Thread中的方法:
1:run();用来定义线程要执行的任务代码
2:start();用来启动线程
3:Thread.currentThread();获取当前线程
4:getId();获取ID
5:getName();setName() 获取/设置名字
6:getState;获取线程状态
7:getPriority();setPriority();获取/设置线程优先级
优先级范围1——10;默认为5
8:Thread.sleep();休眠指定时间
9:join();等待当前线程执行完毕,其他线程再执行
10:Thread.yield();//礼让
*/
MyThread myThread =new MyThread();
myThread.setName("线程1");
myThread.setPriority(4);
myThread.start();
//myThread.join();//当代线程终止
MyThread myThread1 =new MyThread();
myThread1.setName("线程2");
myThread1.setPriority(3);
myThread1.start();
}
}
测试类:
package com.wbc.Thread.多线程.ThreadMethods;
public class MyThread extends Thread{
@Override
public void run() {
try {
Thread.sleep(1000);//休眠
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i=0;i<1000;i++){
Thread thread =Thread.currentThread();//获取当前执行的线程
if(i%10==0){
Thread.yield();//礼让
}
System.out.println(thread.getName()+" "+i);
}
}
}
(三)、单线程与多线程
当在java程序中有几件不相关的事情同时有机会执行.可以在java中创建多个线程,把一些要执行的任务放在线程中执行,这样的话,都拥有让cpu执行的权利。而如果只需要顺序执行的话则不需要创建多个线程
在正式了解多线程之前先了解一下线程的生命周期
线程的生命周期
线程的生命周期是线程从创建到销毁的过程,期间共经历五个状态
新建状态:新建 new Thread(); 处于新建状态,此状态还不能被执行.
调用start()启动线程 让线程进入到就绪状态,
就绪状态:获得到cpu执行权后, 线程进入到cpu执行
运行状态:运行中的线程可以被切换,回到就绪状态, 也可能因为休眠等原因进入阻塞状态
阻塞状态:线程休眠时间到了 回到就绪状态
死亡状态:当线程中所有的任务执行完了, 线程也就自动销毁
线程生命周期示意图
多线程的概念及其优缺点
在一个程序可以创建多个线程,以执行不同的任务
多线程的优点:提高程序的响应 提高cpu的利用率 改善程序结构,将复杂任务分为多个线程,独立运行
多线程的缺点:线程越多占用内存越多 跟踪管理线程cpu开销变大
多线程的使用场景:
多线程访问操作同一个共享数据(例如 买票 、抢购、秒杀)
需要解决操作共享的问题。解决方法:排队+锁 在关键步骤处,多个线程只能一个一个执行
添加同步锁
加锁方式1:
使用synchronized关键字修饰代码块和方法
修饰代码块
同步对象要求: 多个线程用到的必须是同一个对象,可以是java中任何类的对象.
作用: 用来记录有没有线程进入到同步代码块中
synchronized(同步对象/锁){
//同步代码块, 一次只允许一个线程进入
}
以实现Runnable接口为例
任务类:
package com.wbc.Thread.多线程.模拟卖票2Runnable;
public class TicketTask implements Runnable{
int num =10;
@Override
public void run() {
while(true){
synchronized (this){//由于两个线程对应的是同一个任务对象,所以可以使用this,以及num可以不限制为静态的
if(num>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(
Thread.currentThread().getName()
+"买到了第"+num+"张票");
num--;
}
else{
break;
}
}
}
}
}
测试类:
package com.wbc.Thread.多线程.模拟卖票2Runnable;
public class TicketTest {
public static void main(String[] args) {
TicketTask task =new TicketTask();
Thread t1 =new Thread(task);
t1.setName("窗口1");
t1.start();
Thread t2 =new Thread(task);
t2.setName("窗口2");
t2.start();
}
}
修饰方法:
1.锁不需要我们提供了,会默认提供锁对象
2.synchronized如果修饰的是非静态的方法,锁对象是this
synchronized如果修饰的是静态方法,锁对象是类的Class对象
一个类只有一个Class对象.
以继承Thread类为例:
线程类:
package com.wbc.Thread.多线程.模拟卖票1Thread;
public class TicketThread extends Thread{
static int num =10;//模拟票数有10张
static TicketThread tickerThread =new TicketThread();
/*
synchronized 修饰方法时,同步锁对象不需要指定
同步锁对象会默认提供:
1、非静态方法锁对象默认是this,会造成有多个this,两个对象
2、静态方法锁对象是当前类的class对象(类的对象,一个类的对象只有一个)
*/
@Override
public void run(){
while(true){
if(num<=0){
break;
}
print();
}
}
public static synchronized void print(){
if(num>0){
System.out.println(Thread.currentThread().getName()+"买到了第"+num+"张票");
num--;
}
}
}
测试类:
package com.wbc.Thread.多线程.模拟卖票1Thread;
public class TicketTest {
public static void main(String[] args) {
TicketThread t1 =new TicketThread();
t1.setName("张三");
t1.start();
TicketThread t2 =new TicketThread();
t2.setName("李四");
t2.start();
}
}
枷锁方式2:
使用jdk中提供的ReentrantLock类实现加锁 ReentrantLock只能对某一段代码块加锁,不能对整个方法加锁
线程类:
import java.util.concurrent.locks.ReentrantLock;
public class TicketThread extends Thread{
static int num =10;
//使用Thread需要静态保证只有一个锁对象,实现Runnable接口则不需要,因为只有一个任务对象
static ReentrantLock reentrantLock =new ReentrantLock();//加锁释放锁需要手动,如果出现异常不会自动释放,所以需要try{}finally{},确保锁会释放
@Override
public void run() {
while(true){
try{
reentrantLock.lock();//加锁,其底层状态由0转1;
if(num>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(
Thread.currentThread().getName()
+"买到了第"+num+"张票");
num--;
}
else{
break;
}
}finally {
reentrantLock.unlock();//释放锁,底层状态从1转0
}
}
}
测试类:
package com.wbc.Thread.多线程.模拟卖票3ReentrantLock;
public class TicketTest {
public static void main(String[] args) {
TicketThread t1 =new TicketThread();
t1.setName("张三");
t1.start();
TicketThread t2 =new TicketThread();
t2.setName("李四");
t2.start();
}
}
synchronized 和 ReentrantLock区别
相同点:都实现了加锁的功能
不同点:
1.synchronized 是一个关键字,ReentrantLock是一个类
2.synchronized修饰代码块和方法,ReentrantLock只能修饰代码块
3.synchronized可以隐式的加锁和释放锁,运行过程中如出现了异常可以自动释放
ReentrantLock需要手动的添加锁和释放锁,建议在finally代码块中释放锁.
(四)、线程通信问题
经典例题:生产者/消费者问题
生产者(Productor)将产品放在柜台(Counter),而消费者(Customer)从柜台处取走产品,生产者一次只能生产固定数量的产品(比如:1), 这时柜台中不能再放产品,此时生产者应停止生产等待消费者拿走产品,此时生产者唤醒消费者来取走产品,消费者拿走产品后,唤醒生产者,消费者开始等待
此类情况,需要线程与线程之间进行通信,而这种通讯在现阶段一般通过在同步代码块的基础上,使用wait,notify对线程进行控制
生产线程
package com.wbc.Thread.多线程.线程通信.生产者消费者问题;
public class ProductorThread extends Thread{
/*
生产者线程
*/
Counter counter;
public ProductorThread(Counter counter) {
this.counter = counter;
}
@Override
public void run() {
while(true){
counter.add();
}
}
}
消费线程
package com.wbc.Thread.多线程.线程通信.生产者消费者问题;
public class CustomerThread extends Thread{
/*
消费者线程
*/
Counter counter;
public CustomerThread(Counter counter) {
this.counter = counter;
}
@Override
public void run() {
while(true){
counter.sub();
}
}
}
柜台
package com.wbc.Thread.多线程.线程通信.生产者消费者问题;
public class Counter {
/*
柜台:共享数据
*/
int num = 0;//商品的数量
/*
只有一个Counter对象,两个方法都非静态,但用的都是同一把锁
*/
//负责生产商品的方法
public synchronized void add(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(num==0){
num=1;
this.notify();//唤醒消费者线程
System.out.println(Thread.currentThread().getName()+":已生产");
}
else{
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//负责销售的方法
public synchronized void sub(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(num==1){
num=0;
this.notify();
System.out.println(Thread.currentThread().getName()+":已购买");
}
else{
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/*
Thread.sleep(long time);
属于Thread类中的方法
sleep();让线程休眠之后会自动唤醒,并继续排队准备执行
sleep()不会释放锁
obj.wait();
属于Object类的中方法,需要锁对象调用
wait后的线程需要其他线程唤醒(notify();notifyAll();唤醒)
wait()会自动释放锁
*/
运行类
package com.wbc.Thread.多线程.线程通信.生产者消费者问题;
public class Test {
public static void main(String[] args) {
Counter counter =new Counter();//唯一的Counter对象
CustomerThread customerThread =new CustomerThread(counter);
customerThread.setName("消费者");
ProductorThread productorThread =new ProductorThread(counter);
productorThread.setName("生产者");
customerThread.start();
productorThread.start();
}
}
十九、JDBC
(一)、JDBC概述
JDBC(Java DataBase Connectivity)java数据库连接
是一种用于执行SQL语句的Java API,可以为多种关系型数据库提供统一访问, 它由一组用Java语言编写的类和接口组成。
有了JDBC,java开发人员只需要编写一次程序,就可以访问不同的数据库
JDBC API:
供程序员调用的接口与类,集成在java.sql包中
DriverManager类作用:管理各种不同的jDBC驱动
Connection 接口 与特定数据库的连接
Statement 接口 执行sql
PreparedStatement接口 执行sql
ResultSet接口 接收查询结
(二)、JDBC搭建
●建立与数据库连接:
这需要使用DriverManager.getConnection()方法来创建一个 Connection对象,它代表一个物理连接的数据库.
Connection conn = DriverManager.getConnection(URL,USER,PASS); URL:jdbc:mysql://ip(127.0.0.1):端口(3306)/数据库 名?serverTimezone=Asia/Shanghai
USER:用户名(root)
PASS:密码
获得Satement执行sql语句
Statement st = connection.createStatement();
Satement中的方法:
Int executeUpdate(String sql) 用于执行ddl语句和dml(增,删,改)语句 返回 操作的行数
用于执行ddl语句返回0
用于执行dml语句返回操作的行数
ResultSet executeQuery(String sql); 用于执行查询语句 返回一个 ResultSet 集合
获得PrepareStatement执行sql语句
在sql语句中参数位置使用占位符,使用setXX方法向sql中设置参数
PrepareStatement ps = connection.prepareStatement(sql);
PrepareStatement中的方法:
Int executeUpdate() 用于执行ddl语句和dml(增,删,改)语句
返回操作的行数
用于执行ddl语句返回0
用于执行dml语句返回操作的行数
ResultSet executeQuery(); 用于执行查询语句 返回一个ResultSet
关闭与数据库的链接通道
每次操作完成后关闭所有与数据库交互的通道
st.close();
rs.close();
conn.close();
ps.close();
package com.wcb.JDBC;
import com.mysql.jdbc.Driver;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class demo1 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//加载驱动类
Class.forName("com.mysql.cj.jdbc.Driver");//用于加载mysql驱动类
//DriverManager.registerDriver(new Driver());
//建立数据可连接,获得连接对象
String url ="jdbc:mysql://127.0.0.1:3306/schooldb?serverTimezone=Asia/Shanghai";//定义连接sql所需的url
String user ="root";//用户名
String password ="";//密码
//建立了与数据库的连接,获得了一个连接对象。
Connection connection =DriverManager.getConnection(url,user,password);
//发送sql
Statement st = connection.createStatement();//创建一个Statement对象,用于向数据库发送SQL语句。
st.executeUpdate("insert into major(name)value('统计')");//代码执行SQL语句
//关闭数据库连接
st.close();
connection.close();
}
}
(三)、增删改查
在执行SQL语句时推荐采用预编译的模式
因为在删除时,使用字符串连接可以在语句后添加类似于or1==1的逻辑语句进行攻击数据库,导致数据全部删除
而预编译模式则不会,因为占位符?只允许传入一个参数
增删改
package com.wcb.JDBC.增删改;
import java.sql.*;
public class demo3 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
demo3 demo2 =new demo3();
//demo2.save("www","男","2004-3-3", "17914567896", 2);
demo2.update("www3","男","2004-3-3", "17914567896", 2,23);
demo2.delete("6");
}
public void save(String name,String gender,String birthday,String phone,int majorid) throws ClassNotFoundException, SQLException {
//加载驱动类
Class.forName("com.mysql.cj.jdbc.Driver");
String url ="jdbc:mysql://127.0.0.1:3306/schooldb?serverTimezone=Asia/Shanghai";//定义连接sql所需的url
String users = "root";
String password = "";
Connection connection = DriverManager.getConnection(url,users,password);//建立连接
Statement statement =connection.createStatement();//建立对象用于发送sql语句
//执行sql语句
//connection对象中的预编译方法 ?是占位符,表示此处需要一个参数
PreparedStatement ps = connection.prepareStatement("insert into student(name,gender,birthday,phone,reg_time,majorid)value(?,?,?,?,now(),?)");
//传值
ps.setObject(1,name);
ps.setObject(2,gender);
ps.setObject(3,birthday);
ps.setObject(4,phone);
ps.setObject(6,majorid);
//执行
ps.executeUpdate();
//关闭
statement.close();
connection.close();
}
public void update(String name,String gender,String birthday,String phone,int majorid,int index) throws ClassNotFoundException, SQLException{
//加载驱动类
Class.forName("com.mysql.cj.jdbc.Driver");
String url ="jdbc:mysql://127.0.0.1:3306/schooldb?serverTimezone=Asia/Shanghai";//定义连接sql所需的url
String users = "root";
String password = "";
Connection connection = DriverManager.getConnection(url,users,password);//建立连接
Statement statement =connection.createStatement();//建立对象用于发送sql语句
//执行sql语句
PreparedStatement preparedStatement=connection.prepareStatement("update student set name = ?,gender = ?, birthday=?,phone = ?,reg_time=now(),majorid = ? where number =? ");
//传值
preparedStatement.setObject(1, name);
preparedStatement.setObject(2, gender);
preparedStatement.setObject(3,birthday);
preparedStatement.setObject(4, phone);
preparedStatement.setObject(5,majorid);
preparedStatement.setObject(6, index);
//执行
preparedStatement.executeUpdate();
statement.close();
connection.close();
}
public void delete(String num) throws ClassNotFoundException, SQLException{
//加载驱动类
Class.forName("com.mysql.cj.jdbc.Driver");
String url ="jdbc:mysql://127.0.0.1:3306/schooldb?serverTimezone=Asia/Shanghai";//定义连接sql所需的url
String users = "root";
String password = "";
Connection connection = DriverManager.getConnection(url,users,password);//建立连接
Statement statement =connection.createStatement();//建立对象用于发送sql语句
//执行sql语句
PreparedStatement preparedStatement=connection.prepareStatement("delete from student where number =?");
//传值
preparedStatement.setObject(1, num);
//执行
preparedStatement.executeUpdate();
statement.close();
connection.close();
}
}
查
package com.wcb.JDBC.查;
import java.sql.*;
import java.util.ArrayList;
public class Demo4 {
public static void main(String[] args) throws SQLException, ClassNotFoundException {
Demo4 demo4 =new Demo4();
//Student student = demo4.findStudentByNum(10);
//System.out.println(student);
ArrayList<Student> students = demo4.findStudentByGender("女");
for(Student student:students){
System.out.println(student);
}
}
public ArrayList<Student> findStudentByGender(String gender) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.cj.jdbc.Driver");
String url ="jdbc:mysql://127.0.0.1:3306/schooldb?serverTimezone=Asia/Shanghai";//定义连接sql所需的url
String user ="root";//用户名
String password ="";//密码
//建立连接
Connection connection = DriverManager.getConnection(url,user,password);
//预编译
PreparedStatement preparedStatement =connection.prepareStatement("select number,name,gender,birthday,phone,reg_time from student where gender = ?");
//传入数据
preparedStatement.setObject(1, gender);
//查询操作
ResultSet resultSet = preparedStatement.executeQuery();//将查询结构封装到ResultSet类型的对象中 需要将数据封装到指定类型的对象中
//ResultSet对象.next() 如果结果集中存在下一个 .next返回true 否则返回false
ArrayList<Student> students =new ArrayList<>();
while(resultSet.next()){
Student student =new Student();
//获取数据
student.setNum(resultSet.getInt("number"));
student.setName(resultSet.getString("name"));
student.setGender(resultSet.getString("gender"));
student.setBirthday(resultSet.getDate("birthday"));
student.setPhone(resultSet.getString("phone"));
student.setRegTime(resultSet.getTimestamp("reg_time"));
students.add(student);
}
//关闭
preparedStatement.close();
connection.close();
resultSet.close();
return students;
}
public Student findStudentByNum(int num) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.cj.jdbc.Driver");
String url ="jdbc:mysql://127.0.0.1:3306/schooldb?serverTimezone=Asia/Shanghai";//定义连接sql所需的url
String user ="root";//用户名
String password ="";//密码
//建立连接
Connection connection = DriverManager.getConnection(url,user,password);
//预编译
PreparedStatement preparedStatement =connection.prepareStatement("select number,name,gender,birthday,phone,reg_time from student where number = ?");
//传入数据
preparedStatement.setObject(1, num);
//查询操作
ResultSet resultSet = preparedStatement.executeQuery();//将查询结构封装到ResultSet类型的对象中 需要将数据封装到指定类型的对象中
//ResultSet对象.next() 如果结果集中存在下一个 .next返回true 否则返回false
Student student =null;
while(resultSet.next()){
student =new Student();
//获取数据
student.setNum(resultSet.getInt("number"));
student.setName(resultSet.getString("name"));
student.setGender(resultSet.getString("gender"));
student.setBirthday(resultSet.getDate("birthday"));
student.setPhone(resultSet.getString("phone"));
student.setRegTime(resultSet.getTimestamp("reg_time"));
}
//关闭
preparedStatement.close();
connection.close();
resultSet.close();
return student;
}
}