Java基础
一.预备(包的概念)
专业名词:
命名空间
拟人化:防止名字重复
映射到我们java程序中:
com.oracle.A.java 甲骨文
在java中我们直接可以通过创建package来达到包的效果
二.基本数据类型
2.1整形(byte,short,int,long)
2.2字符型(char)
2.3布尔类型(boolean)
只有两个值:true/false
2.4浮点型
了解:科学计数法是十进制的
1234=1.23410(3)
但是计算机是二进制的
15===>二进制 0000 1111 ===>1.1112(0000 0011)
0 0 1 1 =3
0乘以2(3)+ 0乘以2(2)+1乘以2(1)+1乘以2(0) =3
int:32
0000 0000 0000 0000 0000 0000 0000 0000
==================================
1.5=0001.1000
2的-1次方=0.5
2的-2次方=0.25
1.3=1+0.3
有些小数无法使用二进制表示,在十进制里面也有这种情况
1/3约=0.3333333333333333333333333333
=0.3333
很明显第一个更加精确,也叫精度更高
在二进制也有这样无法正确描述的小数,我们就可以使用单精度浮点型和双精度浮点型来表示小数
2.4.1单精度浮点型:4个字节32位
1/3=0.333=3.33*10(-1)=指数位,尾数位,符号位
1.111*2(0111)分为指数位,尾数位,符号位
1个符号位,8个指数位,23个尾数
单精度在定义的时候一定要在后面加f, float f1=0.33f
2.4.2双精度浮点型:8个字节64位
1/3=0.333333333333333333=3.333333333333333333*10(-1)=指数位,尾数位,符号位
1个符号位,11个指数位,52个尾数
存款:1000000000000000
利息:按照0.2和0.2222222222222222222222
双精度在定义的时候可以在后面加d// 1/3+0.222333=?
//精度丢失问题:小数可能无法正确的转为对应的二进制
//后面可以使用Decimal来进行解决
三.Java运算符
3.1赋值运算符
int a=1;
//一定要保证变量初始化
int b=a+1;
int c=a;
3.2算数运算符
package com.ruandy;
/**
* @author Administrator
* @title: Demo1
* @projectName idea_vip2106
* @description: TODO
* @date 2021/8/1820:56
* */
public class Demo1 {
public static void main(String[] args) {
//+
int a=1;
int b=3;
int c=a+b;//住了4个人
System.out.println("加法:"+c);
//我们酒店有个房间叫B6666,那个房间不错,
//我们酒店有个房间叫B6666,我们酒店有个房间叫B6666不错
c=a-b;//住了-2个人
System.out.println("减法:"+c);
c=a*b;
System.out.println("乘法:"+c);
c=a/b;// 1/3
// 1除以3=商0 余数1
// 2除以5=商0 余数2
// 4除以3=商1 余数1
System.out.println("(求商)/:"+c);
c=2%5;//求余数
System.out.println("%:"+c);
}
}
3.3条件和关系运算符
package com.ruandy;
/**
* @author Administrator
* @title: Demo2
* @projectName idea_vip2106
* @description: TODO
* @date 2021/8/18 21:38
*/
public class Demo2 {
public static void main(String[] args) {
int a=1;
int b=2;
//条件和关系运算符的结构都是boolean
boolean flag1= a>b;
System.out.println("a>b:"+flag1);
boolean flag2= a<b;
System.out.println("a<b:"+flag2);
boolean flag3= (a==b);
System.out.println("a==b:"+flag3);
boolean flag4= (a!=b);
System.out.println("a!=b:"+flag4);
boolean flag5= (a>=b);
System.out.println("a>=b:"+flag5);
boolean flag6= (a<=b);
System.out.println("a<=b:"+flag6);
}
}
3.4逻辑运算符
主要针对boolean类型的结果进行运算
boolean b1=true;
boolean b2=false;
b1和b2
b1&&b2: 2者都为true,结果才是true,只要有一个false.那么结构就是false ( 不但,而且)
b1||b2: 2者只要有一个true,结果就是true,除非都为false,结果才是false
! b1 : 如果为true,那么结果为false,如果为false,结果为true
package com.ruandy;
import java.util.Scanner;
/**
* @author Administrator
* @title: Demo3
* @projectName idea_vip2106
* @description: TODO
* @date 2021/8/18 21:53
*/
public class Demo3 {
public static void main(String[] args) {
Scanner in=new Scanner(System.in);
System.out.println("请输入a的值:");
int a=in.nextInt();
System.out.println("请输入b的值:");
int b=in.nextInt();
//获得boolean类型的结果
boolean flag1=a>10;//true
boolean flag2=b<20;//false
System.out.println("&&:"+(flag1 && flag2));//不但 而且
System.out.println("||:"+(flag1 || flag2));//要么..要么
System.out.println("!:"+(!flag1));//
//注意短路问题
//&& 只要有一个是false 那么结果即为false
// 如果a&&b,a为false,那么b将不参与计算
//|| 只要有一个是true,那么结果即为true
// a||b,a为true,那么b将不参与计算
boolean flag3= (a>20) && (b<20);
System.out.println("&&"+ flag3 );
}
}
tips: && 和& 以及 ||和|
一.&&和||是逻辑运算符, &和|是位运算符
二.举例
位运算做的就是二进制之间的计算
3===>0011
4===>0100
&:与运算,都为1才是1,否则为0
3&4
0011
0100
=========
0000
|: 或运算,只有同时为0才是0,其他都是1
3|4
0011
0100
=========
0111
四.循环结构
一定条件下,反复执行的代码块
解决:重复的,有规律的问题
4.1while循环
while(条件表达式){
//语句块
}
//如果条件表达式为true,就会一直执行语句块
//可以联想if(){}
当(山峰没有棱角的时候){
我才和你分手
}
package com.ruandy;
import java.util.Random;
import java.util.Scanner;
/**
* @author Administrator
* @title: HeartChallenge
* @projectName idea_vip2106
* @description: 女神随机一个心跳数值,你来猜,最多猜5次,每次猜的时候,给提示:
* 猜大了,打印提示大了;猜小了,打印提示小了;猜正确了,提示你真棒,不管是否到达5次,直接结
束循环
* @date 2021/8/22 20:27
*/
public class HeartChallenge {
public static void main(String[] args) {
System.out.println("欢迎来到男生女生猜猜猜游戏");
//获得随机的心跳,假设心跳的范围: 70-120
//[70,120]-70=[0,50]
//随机数对象
Random r=new Random();
//随机获得女神的心跳值
// =绩效范围+底薪
int godness=r.nextInt(51)+70;//[0,50]+70
//用户猜的次数
int num=0;//默认一次都没有猜
//设置一个开关,默认是关 false
//键盘扫描对象
Scanner sc=new Scanner(System.in);
while(num<5){
System.out.println("请输入您猜的心跳值");
int code=sc.nextInt();
//三种可能
if(code>godness){
System.out.println("猜大了");
}else if(code<godness){
System.out.println("猜小了");
}else{
System.out.println("猜对了!");
break;
}
//不管是否猜中,都需要计数
num++;
}
//思考:如果5次都没有猜对,提示用户加油.... 可以使用==5或者使用开关的思想
// if(){
//
// }
System.out.println("女神的心跳值:"+godness);
}
}
4.2do…while循环
dowhile:先做(执行)再循环
while:先判定再执行
抽奖:先执行,再看是否需要继续执行直到抽中位置.,…
do{
}while(条件);
package com.ruandy;
import java.util.Scanner;
/**
* @author Administrator
* @title: DoWhileTest
* @projectName idea_vip2106
* @description: TODO
* @date 2021/8/22 21:11
*/
public class DoWhileTest {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
/**
* 当我们定义一个变量放入语句块里面,外部是无法使用的,这样的变量叫局部变量
*/
int code;//就表示咱们家里的那个宝宝,现在我给大家介绍一下
do{
System.out.println("请输入你猜的数字:");
code=sc.nextInt();//宝宝
}while (code!=8888);
}
}
dowhile和while区别与选择
1.while是先判定再执行,dowhile是先执行一次在判定,也就是说dowhile不管条件是否满足,至少能执
行一次
2.应用场景:一般while在做一些循环判定的时候,需要根据条件来执行,比如计算,打印之类的,dowhile
一般在
抽奖,彩票,类似于"直到…才,…"
3.两种循环,大家在选择的时候,都需要去考虑循环的条件是什么
4.3for循环
表达式1
while(条件表达式2){
语句块 4
表达式3
}
while的写法:
int i=1;
while(i<10){
sout(‘’‘’')
i++;
}
for(表达式1:条件表达式2:表达式3){
语句块4
}
for(int i=1;i<10;i++){
打印语句
}
表达式1:通常用来定义变量,比如 int i=0;
条件表达式2:一般用来做判定 比如 i<10
表达式3:一般用来做变量数据变化,类似于 while(){ i++或者i–}
执行顺序: 1 243 243 243…直到2不满足位置
package com.ruandy;
/**
* @author Administrator
* @title: ForDemo1
* @projectName idea_vip2106
* @description: TODO
* @date 2021/8/22 21:49
*/
public class ForDemo1 {
public static void main(String[] args) {
//一.while写法:1243 243 243 243 直到2不满足位置
//表达式1
int i=1;
//条件表达式2
while (i<10){
//语句块4:有的时候可能会有多个
System.out.println("这是while循环i的变化:"+i);
//表达式3
i++;
}
System.out.println("====================================================");
//二.for循环
//死循环:等同于 while(true){}
// for(;;){
// System.out.println(111);
// }
// (表达式1;条件表达式2;表达式3)
// 1243 243 243 直到2不满足为止
for(int k=1;k<10;k++){
//语句块4
System.out.println("这是for循环k的变化:"+k);
}
System.out.println("====================================================");
//1+2+3....100和0+1+2+...100
//while和for
//如果确定循环次数:for,不确定使用while
int sum=0;
for (int m=1;m<=100;m++) {
sum+=m;
}
System.out.println("for循环1+2+3...100="+sum);
//图案一:
//***
//***
//***
//图案二:
//1*1=1
//2*1=2
//3*1=3
//图案三:思考
//*
//**
//***
}
}
4.4break和continue
4.4.1break
break:跳出当前循环,或者可以在指定的位置跳出
如果想跳出指定位置
位置名:循环
{break 位置名}
/**
* @author Administrator
* @title: Demo3
* @projectName idea_vip2106
* @description: TODO
* @date 2021/8/25 21:32
*/
public class Demo3 {
public static void main(String[] args) {
// 1 2 3
for (int i = 0; i <3 ; i++) {
//4
System.out.println("外层循环"+i);
//可以在循环的时候做一些判定,如果满足,即跳出循环
if(i==2){
break;
}
}
System.out.println("------------------------------------------------------");
//直接写break只能跳出当前循环
//flag这个名字随便写,满足变量名的定义即可
flag:for (int i = 0; i <3 ; i++) {
for (int j = 0; j < 3; j++) {
if(i==0 && j==0){
break flag;
}
Sys
}
4.4.2continue
/**
* @author Administrator
* @title: Demo4
* @projectName idea_vip2106
* @description: TODO
* @date 2021/8/25 21:50
*/
public class Demo4 {
public static void main(String[] args) {
//求0-50之间所有的偶数 除以2余数为0
for (int i = 0; i < 10; i++) {
//奇数
if(i%2 != 0){
//如果为奇数,下面不需要执行,代码此次循环执行到这里就可以结束了,下次循环继续
//continue:终止当前的后续操作
continue;
}
//System.out.println("我要耍帅一下,,,,");
System.out.println("偶数为:"+i);
}
System.out.println("-----------------");
//和break一样,也可以使用标志.模拟break
//通过continue跳出当前循环,一般不建议使用,因为break就可以达到这样的效果---[了解]
flag:for (int i = 0; i < 5; i++) {
for (int j = 0; j <5 ; j++) {
if(j==3){
continue flag;
}
}
}
}
}
4.4.3二者区别
- break会跳出当前的整个循环
- continue会跳出当前轮次的后续操作
- break可以使用标志的方式来跳出任意循环,continue可以使用标志去达到break的跳出当前循
环的操作
五.数组
5.1需求
1.比较2个数的大小可以直接使用ifelse
2.比较100个数的大小,联想到循环,但是循环解决的是有规律重复问题
11,22,9,9,…100
问题:没有办法去定位?
5.2定义
用来存放一系列数据类型相同的元素的一个容器
数组是一种引用数据类型,比较简单的数据结构
package com.ruandy;
import java.util.Arrays;
import java.util.Random;
/**
* @author Administrator
* @title: ArrayDemo1
* @projectName idea_vip2106
* @description: TODO
* @date 2021/8/27 20:15
*/
public class ArrayDemo1 {
public static void main(String[] args) {
//1.定义数组:直接赋值法
//类型 变量名
//数组的下标是从0开始
int[] ary1={11,22,9,8,7};
System.out.println("数组第一个位置:"+ary1[0]);
//java.lang.ArrayIndexOutOfBoundsException:数组下标越界
//System.out.println("数组第一个位置:"+ary1[-1]);
//思考:如果我想依次得到所有的值ary1[0] ary1[1] ary1[2] ary1[3]
//数组有长度的概念:初始固定长度
//数组的长度=数组名.length
for(int i=0;i<ary1.length;i++){
System.out.println("数组元素:"+ary1[i]);
}
//2.定义数组
int[] ary2=new int[6];//买一个5个格子书架
//构建一个数组以后,数组里面的默认值:数据类型的默认值
System.out.println(ary2[0]);
//给书架赋值:内容值必须要书架的数据类型一致
ary2[1]=999;
//如果我们想给数组的最后一项赋值,最后一项的下标 index=数组.length-1
ary2[ary2.length-1]=999;//[0,1,2,3,4,5]
//随机赋值再打印(键盘扫描器),循环打印
Random r=new Random();
// 1 243 243....
for(int i=0;i<ary2.length;i++){
//每次循环取值
ary2[i]=r.nextInt(10);//[0,9]
System.out.println("ary2的书架:"+ary2[i]);
}
//3.数组的定义
//表示构建一个数组,初始值分别11,12,13
int[] ary3=new int[]{11,12,13};
//4.数组的工具类Arrays快速打印
System.out.println(Arrays.toString(ary2));
//5.基本类型的数组
// 整形:默认值是0
// 布尔类型:默认值false
// 浮点型:0.0
// 字符型: ''
// 字符串:null
String[] ss=new String[5];
//赋值
ss[3]="新垣结衣";
System.out.println(Arrays.toString(ss));
String[] coolMan={"吴彦祖","彭于晏","易烊千玺","苏焱"};
System.out.println(Arrays.toString(coolMan));
}
}
5.3数组的遍历
package com.ruandy;
/**
* @author Administrator
* @title: ArrayDemo2
* @projectName idea_vip2106
* @description: TODO
* @date 2021/8/27 21:10
*/
public class ArrayDemo2 {
public static void main(String[] args) {
int[] ary={3,8,2,1,4};
//遍历方式1:普通for循环
for (int i = 0; i <ary.length ; i++) {
System.out.println(ary[i]);
}
System.out.println("---------------------------------");
//遍历方式2:增强for循环
//应用场景:一般只关注内容,不关注下标
// for(数组元素类型 变量:你需要遍历的目标数组){
//
// }
//int a=ary[1];
for(int a:ary){
System.out.println(a);
}
System.out.println("---------------");
//遍历方式3:while循环
int k=0;
while(k<ary.length){
System.out.println(ary[k]);
k++;
}
}
}
5.4数组的扩容和拷贝
package com.ruandy;
import java.util.Arrays;
/**
* @author Administrator
* @title: ArrayDemo3
* @projectName idea_vip2106
* @description: TODO
* @date 2021/8/27 21:25
*/
public class ArrayDemo3 {
public static void main(String[] args) {
String[] ary=new String[3];
//赋值
ary[0]="吴彦祖";
ary[1]="彭于晏";
ary[2]="苏焱";
//由于数组是固定长度,如果需要添加元素,那么将数组的长度变大
//Arrays.copyOf(拷贝的目标数组,拷贝以后的新长度);
//拷贝:相当于按照原来的数组进行拷贝,重新建一个新的数组
String[] newAry=Arrays.copyOf(ary,ary.length+1);
//如果需要将原数组扩容:直接将新的数组赋值给旧的就可以
//ary=Arrays.copyOf(ary,ary.length*2);
System.out.println("扩容以后的数组:"+Arrays.toString(newAry));
//添加一个元素:添加到末尾
newAry[newAry.length-1]="易烊千玺";
System.out.println(Arrays.toString(newAry));
//添加到任意位置--->数组的弊端
newAry=Arrays.copyOf(newAry,newAry.length+1);
//newAry[1]="满仔";
// 0 1 2 3 4
// [吴彦祖, 彭于晏, 苏焱, 易烊千玺, null]
// 4=3
// 3=2
// 2=1
System.out.println(Arrays.toString(newAry));
for (int i = 3; i>=1 ; i--) {
newAry[i+1]=newAry[i];
}
newAry[1]="王成博";
//此种做法效率较低,一般如果数组需要添加和删除元素的时候,效率都比较低
//数组适合检索数据(读取值)
System.out.println(Arrays.toString(newAry));
//思考:删除元素,长度再怎么减,不能小于数组最大的
String[] a2=Arrays.copyOf(newAry,newAry.length-1);
System.out.println("长度-1以后:"+Arrays.toString(a2));
//数组的截取
String[] ss1={"高圆圆","新垣结衣","迪丽热巴","贾静雯","杨杨"};
String[] ss2={null,null,null,null};
//System.arraycopy(从哪个数组截取,截取的起始位置,截取到哪个数组,赋值的位置,截取几个长度);
//把ss1里面的第2和第3个元素,放到ss2的后两位
System.arraycopy(ss1,1,ss2,2,2);
System.out.println("截取以后ss2:"+Arrays.toString(ss2));
//数组的暴力复制
String[] ss3=new String[ss1.length];
for (int i = 0; i < ss3.length; i++) {
ss3[i]=ss1[i];
}
System.out.println("暴力赋值:"+Arrays.toString(ss3));
System.out.println(ss3);
}
}
5.5数组的排序
选择,插入,冒泡,快速,二分...
Arrays.sort(数组名)
package com.ruandy;
import java.util.Arrays;
import java.util.Random;
/**
* @author Administrator
* @title: Demo1
* @projectName idea_vip2106
* @description: TODO
* @date 2021/9/1 20:06
*/
public class Demo1 {
public static void main(String[] args) {
int[] ary=new int[5];//0
//构建随机数对象
Random r=new Random();
//赋值
for (int i = 0; i < ary.length; i++) {
ary[i]=r.nextInt(100);
}
System.out.println("排序前:"+ Arrays.toString(ary));
//选择排序
// for(int i=0;i<4;i++){
// for(int j=i+1;j<5;j++){
// //前面的大于后面的,则需要交换
// if(ary[i]>ary[j]){
// //先拿出前面的值
// int k=ary[i];
// //再将后面的值赋值给前面
// ary[i]=ary[j];
// //再将前面的值赋值给后面
// ary[j]=k;
// }
// }
// }
//对于新手来说,可以直接使用工具类来进行排序
Arrays.sort(ary);
System.out.println(Arrays.toString(ary));
}
}
5.6数组案例
双色球
package com.ruandy;
import java.util.Arrays;
import java.util.Random;
/**
* @author Administrator
* @title: DoubleColorBalls
* @projectName idea_vip2106
* @description: TODO
* @date 2021/9/1 21:21
*/
public class DoubleColorBalls {
public static void main(String[] args) {
System.out.println("欢迎来到中国福利彩票中心");
System.out.println("2元做福利,圆你一个暴富梦");
//福利彩票:红色球+蓝色球
// 红色球范围: 1-33 取6个不重复
// 蓝色球范围: 1-16 取1个
// 最低奖: 中一个蓝球号码,即可中五元
Random r=new Random();
//一.蓝色球号码
int blueBall=r.nextInt(16)+1;//[0,15]
System.out.println("第21100期开奖结果 开奖日期:2021-09-02 21:15:00");
System.out.println("蓝球号码:"+blueBall);
//二.红色球号码
//2.1 redball表示存储结果的号码:去目标球中取6个不重复的
int[] redBall=new int[6];
//2.2 目标球
int[] target={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};
//2.3 给每个目标球一个标志:数组每个值默认都是false
boolean[] flag=new boolean[target.length];
//2.4 取一次是dowhile,每次取球都需要使用dowhile进行判定
for (int i = 0; i < redBall.length; i++) {
int index;
//第一次
do {
//随机一个下标:目标球和标志数组的下标
index=r.nextInt(33);//[0,32]
//取一个就需要判断他的标志
}while (flag[index]);
//跳出循环,说明获得的下标是没有被其他人取过的---->说明他是不重复的!
//修改当前的状态
flag[index]=true;
//获取当前下标的号码target[index].然后赋值给当前外层for对应 目标球号码
redBall[i]=target[index];
}
//3.将红球号码排序
Arrays.sort(redBall);
System.out.println("红球号码:"+Arrays.toString(redBall));
System.out.println("==================");
//4.将红球号码扩容一个大小
int[] balls=Arrays.copyOf(redBall,redBall.length+1);
//将蓝色球号码放到新数组的最后一个
balls[balls.length-1]=blueBall;
System.out.println(Arrays.toString(balls));
}
}
六.方法
方法的语法格式
修饰符 返回值 方法名(变量类型1 变量名1,......){
}
package com.ruandy;
import java.util.Arrays;
import java.util.Random;
/**
* @author Administrator
* @title: Demo1
* @projectName idea_vip2106
* @description: TODO
* @date 2021/9/3 20:03
*/
public class Demo1 {
public static void main(String[] args) {
System.out.println("乔峰走在路上遇到了慕容复...");
takeDragon();
System.out.println("乔峰使用降龙18掌将慕容复打退了...");
System.out.println("乔峰走在路上遇到了鸠摩智...");
takeDragon();
System.out.println("乔峰使用降龙18掌将鸠摩智打退了...");
System.out.println("乔峰来到了包子店卖包子");
String bz=makeBaozi("面粉","青菜");
System.out.println("乔峰买的是:"+bz);
//r.nextInt(1000)
}
/**
* 降龙十八掌
*/
public static void takeDragon(){
System.out.println("1.扎马步");
System.out.println("2.双手推开");
System.out.println("3.左边来一下");
System.out.println("4.右边来一下");
System.out.println("5.向前推出去发功");
}
/**
* 做包子
*/
public static String makeBaozi(String mianfen,String meat){
System.out.println("1.和面");
System.out.println("2.加水,疯狂的揉"+mianfen);
System.out.println("3.剁"+meat);
System.out.println("4.蒸包子");
//如果定义了返回值,那么返回值的类型需要和上面类型一致
return meat+"包子";
}
}
package com.ruandy;
import java.util.Scanner;
/**
* @author Administrator
* @title: Demo2
* @projectName idea_vip2106
* @description: TODO
* @date 2021/9/3 20:26
*/
public class Demo2 {
public static void main(String[] args) {
//求1-100的和
Scanner sc=new Scanner(System.in);
System.out.println("请输入您想求多少的累加和");
int code=sc.nextInt();
//循环累加
int sum=mysum(code);
System.out.println("和为:"+sum);
//继续求和
//mysum(1000);
//mysum(10000);
}
/**
* 求和
* @param code
* @return 返回值
*/
//修饰符 static 返回值 方法名(参数)
public static int mysum(int code){
int sum=0;
for(int i=1;i<=code;i++){
sum+=i;
}
return sum;
}
}
猜字符
package com.ruandy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
import java.util.Scanner;
/**
* @author Administrator
* @title: Demo3
* @projectName idea_vip2106
* @description: 猜字母
* 1.系统随机生成4个字符A-Z,可以重复
* 2.用户输入4个字符进行猜
* 3.用户猜对了几个,并且有几个位置也猜对了
* 系统:ABCD
* 用户:CBFG
* 猜对了2个,有1个位置是正确
* @date 2021/9/3 20:39
*/
public class Demo3 {
public static void main(String[] args) {
//1.系统生成4个字符 4个char--char[] ch=new char[4]
//包子 肉包子=做包子();
char[] ch=f1();
System.out.println(Arrays.toString(ch));
//2.用户输入4个字符
char[] input=f2();
//3.比较 result=new int[2] 第一个元素该表示猜对了几个 第二个元素表示位置猜对了几个
int[] result=mycompare(ch,input);
System.out.println("用户猜对了:"+result[0]+"个");
System.out.println("其中有"+result[1]+"个位置也猜对了");
}
public static int[] mycompare(char[] ch,char[] input){
//3.1定义结果数组
int[] result=new int[2];
//3.2 比较
for(int i=0;i<ch.length;i++){
for(int j=0;j<input.length;j++){
//判定
if(ch[i]==input[j]){
//内容相同,说明相等
result[0]++;
//再判定下标
if(i==j){
result[1]++;
}
}
}
}
//3.3 返回结果
return result;
}
/**
* 获得用户输入的4个字符
* @return 返回一个字符数组
*/
public static char[] f2(){
//2.1 键盘扫描对象
Scanner sc=new Scanner(System.in);
//2.2 获得用户输入四个字符
System.out.println("请输入您猜的字符:4个长度");
String in=sc.next();
//2.3 构建返回的字符数组
char[] input=new char[4];
//2.4 将字符串--->字符数组
for (int i = 0; i < input.length; i++) {
input[i]=in.charAt(i);
}
//2.5 返回
return input;
}
/**
* 系统生成4个字符的方法
* @return 一个4个大小的数组
*/
public static char[] f1(){
//1.1 先创建四个大小的字符数组
char[] ch=new char[4];
//1.2 随机数对象
Random r=new Random();
//1.3 随机四个字符 A-Z ---> [65,90]-65= [0,25]
for(int i=0;i<ch.length;i++){
ch[i]= (char) (r.nextInt(26)+65);//[65,90] int
}
//1.4 返回
return ch;
}
}
蔡徐坤大战吴亦凡
package com.ruandy;
import java.util.Random;
/**
* @author Administrator
* @title: Demo4
* @projectName idea_vip2106
* @description: TODO
* @date 2021/9/3 21:42
*/
public class Demo4 {
public static void main(String[] args) {
//1.蔡许坤
int cxk=200;
//2.吴一凡
int wyf=200;
//3.打起来:
fight(cxk,wyf);
}
//战斗方法:一直打,知道其中一方的血量为0
public static void fight(int cxk,int wyf){
//3.1 构建一个随机数,每次roll点
// 如果0: cxk出招
// 如果1: wyf出招
Random r=new Random();
//3.2 一直打
while(true){
//让循环一秒执行一次
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//roll点
int k=r.nextInt(2);//[0,1]
//判定
if(cxk<=0){
System.out.println("吴一凡获得了胜利");
System.out.println("吴一凡剩余血量:"+wyf);
break;
}else if(wyf<=0){
System.out.println("蔡许坤获得了胜利");
System.out.println("蔡许坤剩余血量:"+cxk);
break;
}else{
//根据随机数,判定谁发功
if(k==0){
wyf-=cxk_skill();
} else{
cxk-=wyf_skill();
}
//可以每次攻击以后,显示双方血量
}
}
}
/**
* 蔡徐坤的技能: [5,20]---->-5 [0,15]
* @return 伤害值
*/
public static int cxk_skill(){
System.out.println("==========hello,我是坤坤==========");
System.out.println("吃我一套连招");
System.out.println("唱,跳,rap,篮球");
System.out.println("==========知道我的厉害了吧==========");
Random r=new Random();
return r.nextInt(16)+5;
}
/**
* 吴亦凡的技能:[8,17]-8 [0,9]
* @return 伤害值
* */
public static int wyf_skill(){
System.out.println("==========hello,我是凡凡==========");
System.out.println("吃我一套连招");
System.out.println("发微博,发公告,面很大很宽");
System.out.println("==========word很大,你忍一下==========");
Random r=new Random();
return r.nextInt(10)+8;
}
}
七.面向对象
7.1面向对象的思想
蛋炒饭方法一
1.先烧油
2.打鸡蛋,然后放进去
3.放隔夜饭
4.放一些调料
5.多炒几下
6.放一点小葱
7.一碗香喷喷的蛋炒饭就出锅了
蛋炒饭方法二
1.打开美团
2.选择一个蛋炒饭的店子,下单买饭–
3.外卖小哥把饭给我送来
分析:
* 方法一:需要炒饭,自己一步一步的完成步骤---->面向过程的炒饭
* 方法二:需要炒饭,但是让别人帮我炒,我去找他要---->面向对象的炒饭
面向对象让复杂的问题简单化了
面向对象让我们从执行者变成调用者,我们就是上帝
面向对象可以让细节隐藏(封装)
生活中的面向对象
面向过程 | 面向对象 |
---|---|
自己去买菜 | 委托隔壁班的王阿姨帮我买菜 |
自己开车 | 代驾 |
自己找女朋友 | 委托珍爱网 |
7.2类和对象
分析一下:
32岁的苏大厨: 有年龄,有姓名,他是厨师
28岁的王大厨: 有年龄,有姓名,他是厨师
22岁的陈大厨: 有年龄,有姓名,他是厨师
ps:他们都有共同的属性:姓名,年龄
他们都有共同的行为:会做饭
上面这一类厨师标准A{
1.姓名
2.年龄
3.会做饭
}
7.2.1类和对象的定义
类:表示具有共同的属性和行为的一类事物,用来描述一类具有共同的属性和行为(模板)
对象:一个类的具体体现,客观存在的事物
类: 厨师
对象:32岁的苏大厨
类和对象的关系:对象是类的具体体现,类是对象的抽象表示
Class Teacher{
//姓名1
//年龄2
//教龄3
//教书4
//性别
}
7.2.2如何使用类和对象
7.2.2.1 类的组成
属性:变量来表示,一般使用成员变量(类里面直接定义的变量,不能在方法里定义),比如厨师的姓名,年龄
行为:比如我们厨师做饭,可以使用方法来表示
7.2.2.2语法格式
package com.ruandy;
/**
* @author Administrator
* @title: Chef
* @projectName idea_vip2106
* @description: TODO
* @date 2021/9/5 20:50
*/
public class Chef {
//属性
String name;
int age;
//行为
//做饭
public void cook(){
System.out.println("厨师会做饭");
}
//打印属性的行为
public void printAttr(){
System.out.println("属性name="+name);
System.out.println("属性age="+age);
}
}
测试类
package com.ruandy;
/**
* @author Administrator
* @title: Demo1
* @projectName idea_vip2106
* @description: 使用厨师类
* @date 2021/9/5 20:53
*/
public class Demo1 {
public static void main(String[] args) {
//new new对象--->分配--->必然存在于计算机世界中...就是具体的表现形式
//构建对象,分配内存,初始化属性
//对象是类的具体表现形式
Chef ch=new Chef();
// 当构建对象以后,对象的属性会默认赋初值
// 打印属性:调用打印方法
ch.printAttr();
//修改属性值
ch.name="苏大厨";
ch.age=22;
//打印属性
ch.printAttr();
System.out.println(ch);//打印的是类名+hashcode值的16进制表示
//调用方法
}
}
7.2.2.3关于一些变量的词汇
class A{
int i;
}
成员变量:定义在类里面的变量
实例变量: 创建的对象的变量
A a=new A()
a.i
局部变量: 部门,某一个区域使用的
class A{
int k=1;
public void f1(){
int i=0;
}
public void f2(){
int j=0;
}
}
全局变量
7.3构造器
7.3.1无参构造器
构造器,构造函数,构造方法
语法:
a.构造方法的名与类名一致
b.构造方法不需要声明返回值
c.无参数构造方法没有参数
d.构造方法会在new对象的时候自动创建,不需要显示调用
构造器一般用来做初始化
package com.ruandy;
/**
* @author Administrator
* @title: Student
* @projectName idea_vip2106
* @description: TODO
* @date 2021/9/5 21:50
*/
public class Student {
//属性
String name="123";//姓名
String num;//学号
int age;
//无参数构造方法
public Student(){
System.out.println("这里是无参数构造器");
//初始化属性值
name="软帝龙哥";//null
num="11111";//null
age=22;//0
}
//行为
public void study(){
System.out.println(name+"爱学习");
}
//打印属性
public void printAttr(){
System.out.println("我的名字:"+name);
System.out.println("我的学号:"+num);
System.out.println("我的年龄:"+age);
}
}
测试类
public class Demo3 {
public static void main(String[] args) {
//new对象: 会自动调用构造方法
Student s=new Student();
//Random r=new Random();
s.printAttr();
}
}
7.3.2有参构造器
//this:当前对象
public Person{
String name;
public Person(String name){
//this.name表示类对象的属性name,不是局部变量的name,这样有助于我们去区分二者
this.name=name;
}
}
public class Demo{
public static void main(String[] args){
//调用有参数构造器,将参数赋值给成员变量
Person p=new Person("李大锤");
System.out.println(p.name);
}
}
问题
1.new对象会默认调用构造器,但是我们之前没有构造器,那该怎么调用呢
因为所有类都会有默认的无参数构造器,里面是空的
2.如果只有 有参数构造器,没有无参数会出现什么样的问题
如果只有 有参数构造器的话,那么调用的时候就必须遵循有参数构造器,默认的无参数就不在生效
建议大家在写的时候写一个无参数
7.3.3代码块
package com.ruandy;
/**
* @author Administrator
* @title: Person
* @projectName idea_vip2106
* @description: TODO
* @date 2021/9/8 20:12
*/
public class Person {
String name;
int age;
//代码块:也是默认执行的,在构造器之前
//对于一些重复构造的,确定数据的进行统一初始化
{
System.out.println("这是代码块");
this.name="哈哈";
}
//定义无参数构造器
public Person(){
System.out.println("这是Person的无参数构造器");
this.name="王麻子";
this.age=20;
}
//定义有参数构造器
public Person(String name,int age){
//这种写法的用处:
//有些属性我们可以直接赋值固定即可,而有些属性需要赋值参数
//通过this()直接调用无参数构造器
this();
System.out.println("这是Person的有参数构造器");
this.name=name;
this.age=age;
}
}
7.4方法的重载
定义
a.方法名相同
b.参数类型或者参数的个数不一致
c.重载发生在本类中
好处
a.多态的表现形式
b.可以定义同一种方法去定义不同的行为
package com.ruandy;
/**
* @author Administrator
* @title: Chef
* @projectName idea_vip2106
* @description: TODO
* * @date 2021/9/8 20:30
*/
public class Chef {
String name;
//做蛋炒饭
public void cook(String egg,String rice){
System.out.println("先炒蛋,再炒饭,一碗蛋炒饭就出锅了...");
}
//做鸡蛋瘦肉炒饭
public void cook(String egg,String rice,String meat){
System.out.println("先炒蛋,再炒肉,然后放入米饭,一碗鸡蛋瘦肉炒饭就出锅了");
}
int cook(String egg,int num){
//可以在方法里面去调用其他方法
this.cook("鸭蛋","东北大米");
System.out.println("测试炒饭1");
return 1;
}
}
关键字:this
1.可以通过this.属性或者方法访问自己的属性和行为
2.可以在构造器里面通过 this()
7.5继承
子类去扩展一个基本类,扩展的可能输属性,扩展行为
1.提高了代码的复用性
2.提高了代码的维护性
3.牵一发而动全身,代码的耦合性增加了
美女类: Beauty
-属性:名字,年龄
-行为:会打扮
package com.ruandy;
/**
* @author Administrator
* @title: Beauty
* @projectName idea_vip2106
* @description: TODO
* @date 2021/9/8 21:01
*/
public class Beauty {
//姓名
String name;
//年龄
int age;
//无参数构造器
public Beauty(){
System.out.println("这是父类的无参数构造器");
}
//有参数构造器
public Beauty(String name,int age){
System.out.println("这是父类有参数构造器");
this.name=name;
this.age=age;
}
//打扮
public void dress(){
System.out.println("美女都会打扮");
}
}
清纯美女类: PureBeauty
-属性:名字,年龄,风格
-行为:会打扮,吃饭
package com.ruandy;
/**
* @author Administrator
* @title: PureBeauty
* @projectName idea_vip2106
* @description: TODO
* @date 2021/9/8 21:01
*/
//更加具体化
public class PureBeauty extends Beauty{
//风格
String style;
//新增一个和父类同名的属性,访问的时候按照的是就近原则:现在本类中,本类找不到再去父类找
//int age;
public PureBeauty(){
//隐藏关键字
super();//默认是隐藏的,super()不管是否有参数,必须写到构造器里面第一行
System.out.println("子类的无参数构造器");
}
//有参数
public PureBeauty(String style){
super();//默认是隐藏的,super()不管是否有参数,必须写到构造器里面第一行
System.out.println("子类的有参数构造器");
this.style=style;
}
//有参数
public PureBeauty(String name,int age,String style){
//可以通过构造器显示的调用指定的构造器
//super();默认的无参数构造器不在存在
//调用的方法是根据参数类型匹配的,本质还是方法的调用
super(name,age);
this.style=style;
}
}
甜美美女类:SweetBeauty
-属性:名字,年龄,风格
-行为:会打扮
在原有类(基类)的基础上,产生一个新的类,在新的类中可以访问原有类中的(非私有化)属性和行为,
并且自己可以新增一些属性和行为,这个过程就叫继承,关键字是extends
1.Java里面类是单继承的
2.子类可以直接使用父类非私有化的属性和行为
3.继承提高了代码的复用性
4.提高了代码的可维护性
5.缺点:继承让各个类产生了关联,类的耦合性增加了,削弱了子类的独立性
6.构造器是不能继承
package com.ruandy;
/**
* @author Administrator
* @title: BeautyDemo
* @projectName idea_vip2106
* @description: TODO
* @date 2021/9/8 21:23
*/
public class BeautyDemo {
public static void main(String[] args) {
//继承中关于父子构造器的调用
// 1.new子类的时候,会默认调用父类的无参数构造器,除非显示声明调用有参数
// 2.可以在子类中使用关键字super显示的去调用父类的构造器或者属性,行为
PureBeauty pb=new PureBeauty();
//赋值
// pb.style="清纯";
// pb.name="高圆圆";
// pb.age=33;
//pb.dress();
}
}
7.6this()和super()
%=######super()和this()必须要放在构造器里面的第一行!!!
this:
a.在方法中,可以通过this.属性访问本类的属性,特别是当方法中有变量的名称和属性一致的时候,可
以进行区分
b.可以通过this()去访问构造器
super:
a.在子类中使用.可以在方法里面访问父类的属性,比如我们子类和父类属性名一致的时候,可以直接
访问父类的非私有属性
b.在子类的构造器中访问父类的构造器
7.7重写
重写是发生在父子类之间,原则:让子类的方法变得更加优化
2同: 方法名相同,参数类型和参数个数相同
2小:返回值:如果是基本数据类型,必须要一致,如果是非基本数据类型,范围要更小(类型更加
具体化)
异常类型(后面再说)
1大:访问类型应该更加大(宽)(后面再说)
class A{}
class B extends A{}
//父类
public A test(int a,int b){
return null;
}
//子类
@Override
public B test(int a,int b){
return null;
}
7.8重写和重载的区别
1.重写发生在父子类的方法
2.重载发生在本类中
3.重载方法名相同,参数类型和个数不一样
4.重写:2同2小1大原则
5.建议在使用重写的时候,用@Override语法去检查
7.9多态
图片里的是谁
1.人
2.美女
3.新垣结衣同一事物,表现出来的不同形态
向上造型-Java
里氏替换原则:在软件设计中,将讲一个子类实例赋值给父类对象,那么父类型变量可以直接使用属性
和行为,不会产生错误,反过来则不一定成立,他是软件开发实现开闭原则的重要依据
class 动物{}
class 狗 extends 动物{
//啃骨头是狗的方法
}
动物 a=new 狗() ;//√ 狗是动物么?
a.啃骨头()//错误
狗 b=new 动物() ;//× 动物是狗么?
7.10关键字
实例变量 instanceof
实例变量 instanceof 类型
判断实例变量是否是某个类型
7.10.1static
静态修饰符
a.被类对象共享
b.可以直接使用类名来调用静态属性或者方法
c.存放在方法区
7.10.1.1修饰变量
多个类变量数据是共享的,访问的时候,可以直接使用类名.静态属性来访问
7.10.1.2修饰方法
一般在一些工具类进行使用,因为他的加载不需要构建实例
7.10.1.3修饰代码块
//代码块是什么调用的:new
{
}
static{
//一般用来加载初始化的一些内容
}
7.10.2final
不可再分/叶节点/最终的/最后的
a.修饰类不能继承
b.修饰方法不能被重写
c.修饰变量不能被修改
修饰变量初始化:
1.可以直接赋值(new对象的时候赋值)
2.也可以在构造器里面赋值
如果想定义一个常量值,建议修饰为如下,static和final并没有先后顺序,而且常量名全部给我大写!
static final String USER_NAME=“张三”;
final static String PWD=“123456”;
7.10.3public
a.修饰为public的,在当前项目任何地方都可以访问
b.可以修饰类,属性,方法
7.10.4protected
一般修饰为protected非同包是不能访问的,但是可以通过继承去访问
tips:一般修饰为protected的方法,潜意识(是希望子类去重写)
7.10.5private
私有的:只能自己访问
访问控制符 | 同包当前类 | 同包不同类 | 非同包子类 | 非同胞非子类 |
---|---|---|---|---|
public | T | T | T | T |
protected | T | T | T | F |
缺省的(default) | T | T | F | F |
private | T | F | F | F |
7.11对象数组
区别于基本类型数组
语法格式
对象类型[] 数组名=new 对象类型[数组的长度]
//直接赋值法同理
7.12JavaBean规范
JavaBean就是一个类,只有遵循这个规范的Java类,才能称之为Javabean
7.12.1规则
a.必须有公共无参数构造器
b.所有的属性都必须是私有的
c.为所有的属性提供公共的get/set方法
d.应该要实现序列化接口
e.javabean的类必须是public
7.12.2应用场景
a.项目的实体对象都是Javabean
b.el表达式获取属性,对象必须要满足规范
c.Spring等框架的属性输入,修改,获取
7.13Object类
所有类的超级父类
7.13.1toString()
打印一个类.默认名调用该类的toString(),当我们需要打印的数据变得好看的时候,可以重写该方法
7.13.2hashcode()
7.13.3equalse()
默认的equals方法他是直接使用==比较的
但是实际上我们一般用来比较内容,不关注地址
重写父类的equals方法,进而达到比较2个对象,只需要比较他们的内容是否一样即可注意:实际上我们在构建一个javabean类的时候,建议大家重写equals和hashcode2个方法
7.14抽象类
美女
a.好看的
b.肤白貌美大长腿
c.腰细的
d.心动的
e.胖的非常凶的
美女只是一个统称,一个概念,他是抽象的.不具体,每个人的脑海里的美女的标准是不一致的,只有当
我们具体的描述的时候,才会有具体的概念
1.美女是抽象的
2.假设我们知道美女有打扮的行为
不同的美女打扮行为可能不一致的
设置为抽象类:
1.抽象的关键字: abstract
2.此关键字可以修饰类和方法
3.抽象类是不能直接实例化的public abstract class Beauty { //打扮:直接定义具体的行为的--->行为被固定,美女是抽象的,美女可能不同的打扮行为 public abstract void dress(); //注意 //1.抽象类里面可以有非抽象方法和抽象方法,也可以没有 //2.有抽象方法的类一定是抽象类(普通类里面不能声明抽象方法) //3.抽象类是不能直接new实例化 public void f1(){ System.out.println("非抽象方法"); } }
7.15接口
7.15.1生活中的接口
a.数据线
b.银联支付
c.插座
都是按照共同,统一的需求进行定制的
7.15.2定义
接口就是一种公共的标准规范,只要符合规范,大家都可以通用
Java中的接口体现在:行为的抽象
7.15.3语法格式
public interface 接口名{
//抽象方法1();
//抽象方法2();
//...
}
interface 插座接口{
能匹配插口();
能正常通电();
}
interface 美女接口{
int i=1;
打扮();
吃饭();
}
7.15.4特点
a.接口不能实例化
b.接口里面的方法都是抽象的,接口的里面的变量都是常量,默认相当于修饰为static final
c.要想使用一个接口,需要使用一个类去implments接口名
d.一个类可以实现多个接口 (一个类只有一个亲爹(父类),但是可以有多个干爹)public class A implements 接口1,接口2,接口3....{ //实现所有的接口的所有抽象方法 }
e.Java里面只有单继承?----->Java里面类
interface A{} interface B{} interface C extends A,B{}
f.接口里面的方法都默认修饰为 public!!!
7.15.5关于继承和实现
1.功能上面
a.继承:是为了代码的复用
b.实现:可以理解为功能的增强,也可以在"一定程度"上面代码的复用
2.从代码的设计思想
a.继承是单继承的,功能就比较难去进行扩展,而且继承的耦合性增加了
b.实现:可以多实现,就像一个机器的多个零件一样,可以去不同的地方去买
实际中更多的2中的组合使用
7.16内部类
7.16.1为什么要使用内部类
a.通过内部类,可以变相的实现多继承
b.通过内部类,可以将一个类的访问权限降到最低
c.通过内部类,可以减少当前包类的个数
7.16.2语法
//内部类:在一个类中定义一个类
class A{
//属性
int age;
//方法
public void f1(){}
class B{
}
}
7.16.3分类
7.16.3.1成员内部类
成员内部类在类中相当于个属性或者方法,把他当做类的一个成员即可
package com.ruandy;
/**
* @author Administrator
* @title: Outer
* @projectName idea_vip2106
* @description: TODO
* @date 2021/9/22 20:10
*/
public class Outer {
private double salary;
String name;
String name1="王麻子";
public void eat(){
System.out.println("吃饭");
//外部类访问内部类的属性"不能直接访问的!
//可以通过构建内部类对象--->内部的属性和方法
A a=new A();
a.age=22;
}
//定义成员内部类
class A{
int age;
String name1="王大锤";
public void drink(){
//内部类可以直接访问外部的属性和方法
name="张三";
eat();
System.out.println(name1);//大锤
System.out.println("这是内部类的成员方法drink");
}
}
class B{
}
}
7.16.3.2局部内部类
//局部变量一般指在方法里面的变量
//局部内部类一般指定义在方法里面的类
class A{
public void f1(){
class B{
}
}
}
7.16.3.3静态内部类
class A{
static class B{
}
}
7.16.3.4匿名内部类
a.前提:必须要存在一个类(普通类或者抽象类)或者接口
b.没有名字的类
new 已经存在前提(){
//重写方法
}
//普通类
class A{
}
new A(){};//普通类的匿名内部类
//抽象类
abstract class B{
public abstract void f1();
}
new B(){
@Override
public void f1(){
}
};//抽象类的匿名内部类-->相当于自己定义了一个类去继承抽象类,重写抽象方法,然后new,但是这个类没有名字
//以上2种写法是一个java语句,他是不能单独存在的,一般在类里面或者方法里面
//接口....
package com.ruandy;
/**
* @author Administrator
* @title: PeopleTest
* @projectName idea_vip2106
* @description: TODO
* @date 2021/9/22 20:39
*/
public class PeopleTest {
public static void main(String[] args) {
Hunks h=new Hunks();
h.play();
//---------------------------------
//可以使用匿名内部类的语法
//同样的重写play方法,达到了和Hunks类同样的作用,但是我们没有通过
//关键字class来进行定义: 他就相当于Person类的子类???
// 他没有名字,它相当于一个类=--->匿名类
// 而该类在方法里面--局部内部类
// 匿名内部类!
// 1.比较适合简单的重写!
// 2.适合只使用一次(局部)
new Person(){
//重写方法
@Override
public void play(){
System.out.println("新的猛男的玩方法");
}
};//这就是一个类!!!并且直接new了
}
}
八.API
8.1常用工具类-API
8.1.1Math
System.out.println("向上取整:"+Math.ceil(27.1));//28.0
System.out.println("向上取整:"+Math.ceil(-27.1));//-27.0
System.out.println("向下取整:"+Math.floor(27.1));//27.0
System.out.println("向下取整:"+Math.floor(-27.1));//-28.0
System.out.println("四舍五入:"+Math.round(27.1));//27
System.out.println("四舍五入:"+Math.round(-27.1));//-27
System.out.println("绝对值:"+Math.abs(-27.1));//
//返回一个0.0-1.0的随机数
System.out.println("求随机数:"+100*Math.random());
System.out.println("求最大值:"+Math.max(1,2));
8.1.2BigDecimal
//--------------------------
//构造器:
//new BigDecimal(int a)
//new BigDecimal(String s)
//new BigDecimal(double d);//会有精度丢失问题
//解决精度丢失的问题
BigDecimal b1=new BigDecimal("2.8");
BigDecimal b2=new BigDecimal("1.2");
//+ - * /
//对应的方法
//此方法会返回一个新的BigDecimal,原来的b1和b2无影响
System.out.println("加法:"+b1.add(b2));
System.out.println(2.8-1.2);
System.out.println("减法:"+b1.subtract(b2));
System.out.println("乘法:"+b1.multiply(b2));
//小数做除法,需要制定保留几位小数,而且需要制定模式
System.out.println("除法:"+b1.divide(b2,5,BigDecimal.ROUND_UP));
//专门制定小数位
BigDecimal b=new BigDecimal("100.231312414");
b=b.setScale(0, RoundingMode.FLOOR);
System.out.println(b);
8.1.3System
//System
//获得当前的时间毫秒数,返回的是一个long类型
// 1小时=1*60*60*1000 表示一个小时的时间毫秒数
//格林威治皇家天文台
// 1970-1-1 0:0:0 到 2021-9-22 21:43:44
//1970+68=2038 -1-1
System.out.println("当前的时间毫秒数:"+System.currentTimeMillis());
for (int i = 0; i < 100000; i++) {
b.divide(b);
}
System.out.println("当前的时间毫秒数:"+System.currentTimeMillis());
System.exit(0);//强制退出
8.2值传递
Java里面只有值传递!
1.对于基本类型和String类型(直接赋值)来说,传递是具体的内容值
2.对于引用类型(new)或者数组来说,传递的是地址值Java里面只有值传递!
1.对于基本类型和String类型(直接赋值)来说,传递是具体的内容值
2.对于引用类型(new)或者数组来说,传递的是地址值
package com.ruandy;
/**
* @author Administrator
* @title: Question
* @projectName idea_vip2106
* @description: TODO
* @date 2021/9/22 21:47
*/
public class Question {
public static void main(String[] args) {
String name="大锤";
int[] ary={1,2,3};
f1(name,ary);
Person p=new Person();
System.out.println(name);
System.out.println(ary[1]);//
}
private static void f1(String name,int[] ary) {
//System.out.println("---"+name);
name="麻子";
ary[1]=4;
//System.out.println("==="+name);
}
}
8.3异常
异常
概念:异常就是程序出现不正常的情况
体系结构
error:严重问题,不需要处理(java虚拟机无法解决的严重问题,jvm系统内部错误,资源耗尽)
Exception:称为异常类,它表示程序本身可以处理的问题
public class ErrorDemo01 {
/*
Student s= new Student();
栈区 堆区
栈区:(基本类型和对象引用)
1.每个线程都包含一个栈区,栈中值保存基本数据类型的对象和自定义对象的引用
2.每个栈中的数据都是私有的,其他栈不能访问
3.栈区分为三个部分:基本类型变量区 执行上下文 操作指令区
堆区:(都是一个new的对象)
1.存储的全部都是对象,每个对象都包含一个与之对应class信息
2.jvm只有一个堆区被所有线程共享,堆区中不存放基本数据类型和对象的引用
只存放对象本身
*/
public static void main(String[] args) {
//StackOverflowError 栈溢出
// main(args);
//OutOfMemoryError 堆溢出
Integer[] arr=new Integer[1024*1024*1024];
}
}
/*
Exception:称为异常类,它表示程序本身可以处理的问题
RuntimeException:(运行时异常)在编译期是不检查的,运行出现问题之后,需要我们回来修改代码
ArrayIndexOutOfBoundsException--数组下标越界异常
ClassCastException---类型转换异常(强行把猫转为狗)
NullPointException---空指针异常(在对null的东西进行.操作)
NumberFormatException---数据转换异常(把字符串转为数)
非RuntimeException:(编译期异常)在编译期就必须处理的,否则程序不能通过编译,就更不能正常运行
FileNotFoundException
*/
public class ExecptionDemo01 {
public static void main(String[] args) throws FileNotFoundException {
//运行期异常
// int[] arr=new int[5];
// arr[5]=10;
// System.out.println(arr[5]);
// //编译期异常 FileNotFoundException
// File f=new File("D://demo.txt");
// //下列代码有一个编译期异常需要马上处理,不然编译不通过
// FileReader fr=new FileReader(f);
int a=Integer.parseInt("rr");
}
}
/*
编译期异常的处理方式
方式一:
try-catch-finally(自己在处理异常)
try:捕获异常的第一步,使用try{}语句块来选定捕获异常的范围,将可能出现异常的代码放在try中
catch:在catch语句块中是对异常对象进行处理的代码,每个try语句块可以伴随一个或者多个catch语句
用于处理可能产生的不同类型的异常对象
finally:为异常处理提供一个统一的出口,上述catch不管走不走,finally里面的都会执行
方式二:
throws +异常类型
*/
public class ExceptionDemo02 {
public static void main(String[] args) {
File f=new File("D://demo.txt");
//下列代码有一个编译期异常需要马上处理,不然编译不通过
try{//可能出现异常的代码
FileReader fr=new FileReader(f);
throw new FileNotFoundException();//加一个FileNotFoundException()
// throw new IOException();//加一个IOException
}catch(FileNotFoundException e){//try代码块中可能出现的异常
System.out.println("FileNotFoundException");
e.printStackTrace();//将异常打印在异常控制台
}catch( IOException e){
System.out.println("IOException");
e.printStackTrace();
}catch (Exception e){
System.out.println("Exception");
e.printStackTrace();
}finally{
System.out.println("finally");
}
}
}
package day1020;
/*
方式二:(将异常抛出去,给调用方法的人去解决)
throws +异常类型
*/
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
public class ExecptionDemo03 {
public static void main(String[] args) throws FileNotFoundException {
File f=new File("D://demo.txt");
FileReader fr=new FileReader(f);
}
}
/*
用户自定义异常
*/
public class ExecptionDemo04 {
public static void main(String[] args) {
int score=-10;
if(score>=0){
System.out.println("登记分数");
}else{
throw new MyException("分数不合法");
}
}
}
/*
如何自定义异常
1.继承于现有的异常结构RuntimeException
2.提供一个重载的构造器
*/
class MyException extends RuntimeException{
MyException(){
}
MyException(String str){
super(str);
}
}
九.集合
/*
java中分为Collection map两种体系
Collection接口
List接口 :元素有序,可重复的集合(动态数组)
ArrayList
LinkedList
Vertor
Set接口:元素无序,不可重复的集合
如果需要通过接口创建对象:
1.创建接口实现类对象,使用多态
父类型 对象名=new 子类对象();(向上造型)
2.匿名内部类 (不适用于集合,因为需要重写的方法太多)
1.Collection接口中方法的使用
1.add(Object o);将元素o添加到集合中
2.addAll(Collection c);将c中所有的元素添加到集合中
3.remove(Object o)
*/
9.1Collection
public class CollectionDemo01 {
public static void main(String[] args) {
//add(Object o);将元素o添加到集合中
Collection c=new ArrayList();
c.add("成博");
c.add(new Date());
c.add(12);
c.add(3.14);
c.add(true);
System.out.println("c"+c);
//addAll(Collection c);将c中所有的元素添加到集合中
Collection c1=new ArrayList();
c1.addAll(c);//将c中所有元素添加到c1中
c1.add(12);
//3.remove(Object o)
c1.remove(3.14);//将集合中的3.14删除/移除
c1.add("c1");
System.out.println("c1"+c1);
Collection c2=new ArrayList();
c2.add(12);
c2.add("成博");
c2.add("飞飞");
System.out.println("c2"+c2);
//4.removeAll
// c2.removeAll(c1);//取c2的差集(将c2中与c1相同的元素删除)
c1.removeAll(c2);//取c1的差集(将c1中与c2相同的元素删除)
System.out.println("remove-All-c1"+c1);
System.out.println("remove-All-c2"+c2);
}
}
/*
1.size();//获取元素的个数
2.clear();清空集合元素
3.isEmpty();判断当前集合是否为空
判断当前集合中是否包含指定的元素
4.contains(Object o);判断o元素是否在集合中存在
在判断的时候,先调用o对象所在类的equals方法,通过equals进行比较
5. containsAll(Collection c)
判断c所有的元素是否都在当前集合中
6.retainAll(Collection c)交集,获取当前集合与c集合中的交集,并返回给当前集合
*/
public class CollectionDemo02 {
public static void main(String[] args) {
int[] arr=new int[10];
//arr.length 属性--数组中表示长度的是属性
System.out.println(arr.length);
String s="12";
//s.length() 方法--字符串中表示长度的是方法
System.out.println(s.length());
Collection c=new ArrayList();
c.add(11);
c.add(123);
System.out.println("clear之前集合元素个数"+c.size());
c.clear();//清空集合元素
System.out.println("clear之后集合元素个数"+c.size());
System.out.println(c.isEmpty());//true
}
}
9.2List
/*
ArrayList LinkedList Vector的异同?谈谈你的理解
ArrayList底层是什么?扩容机制是什么?Vector和ArrayList最大区别
线程安全问题: ArrayList LinkedList都是线程不安全的,线程安全的Vector;前两者执行效率高
数据结构: ArrayList Vector都是基于动态数组的数据结构,LinkedList基于链表的数据结构
随机访问:get set ArrayList比较占优势,因为LinkedList要移动指针
删除和插入:LinkedList比较占优势,因为ArrayList要移动数据
ArrayList底层是数组,扩容1.5倍;Vertor扩容的2倍
*/
9.2.1LinkedList
/*
LinkedList:
对于频繁的插入或者删除元素的操作效率高!
底层定义了Node类型的first 和last;记首末元素的
private static class Node<E> {
E item;//当前元素的内容
Node<E> next;//下一个元素的内容
Node<E> prev;//上一个元素的内容
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
*/
public class LinkedListDemo01 {
public static void main(String[] args) {
LinkedList ls=new LinkedList();
ls.add("飞飞");
ls.add("疏疏");
ls.add("阳阳");
System.out.println("原始集合内容"+ls);
//void addFirst(Object obj) 在集合的开头插入obj元素
ls.addFirst("畅畅");
System.out.println("调用addFirst后:"+ls);
//void addLast(Object obj) 在集合的末尾插入obj元素
ls.addLast("小泽玛利亚");
System.out.println("调用addLast后"+ls);
//Object getFirst()返回集合中的第一个元素
System.out.println("第一个元素为:"+ls.getFirst());
//Object getLast()返回集合中的最后一个元素
System.out.println("最后一个元素为:"+ls.getLast());
}
}
9.2.2ArrayList
ArrayList:
List接口的主要实现类,线程不安全,效率高
/*ArrayList 底层实现思路:
JDK 7
ArrayList list=new ArrayList();底层创建一个长度为10的数组Object[] element=new Object[10];
list.add("1233");//element[0]="1233";
....
list.add(123);//element[9]=123;添加第十个元素
list.add(999);//如果此次添加导致底层element数组容量不够了,则自动扩容,并且将前面的内容拷贝过来
默认情况下,扩容原来容量的1.5倍
JDK 8
ArrayList list=new ArrayList();底层创建一个数组Object[] element={};
list.add(123);//第一次调用add添加,底层才会创建长度为10的数组,并将123赋值到element[0]中
后续与JDK7是一样的
*/
9.2.3Vector
/*
Vector
是一个古老的集合类,JDK1.0就有了,大多数操作与ArrayList相同
区别就在于Vector线程安全的,ArrayList线程不安全的,效率低
JDK7和8通过Vector创建对象的时候,底层创建一个长度为10的数组
在扩容的时候,默认扩容是元素组长度的2倍
*/
9.3Map接口
/*
Map接口:双列数据,保存具有映射关系的"key-value"的集合(用于保存一对一对的数据,键值对)
Hashtable是一个古老的Map实现类,JDK1.0就提供了,不同于HashMap,Hashtable是线程安全的
Hashtable实现原理HashMap相同,功能也相同,
Map常用方法
1.添加/删除/修改
Object put(Object key,Object value)向集合中添加|"key-value"元素
void putAll(Map mp)向当前集合添加mp集合中所有的元素
Object remove(Object key)移除指定key对应key-value键值对,并返回value
2.元素查询
Object get(Object key) 获取指定key的value
*/
9.3.1HashMap
/*
HashMap 底层的实现原理
JKD7和8底层实现方面有什么不同
1.底层结构 JDK 7:HashMap底层是数组+链表
JDK 8:HashMap底层是数组+链表+红黑树
2.JDK7:第一次创建对象时,底层创建长度为16的数组
JDK8:第一次创建对象时,底层没有创建长度为16的数组
第一调用put方法时,底层创建长度为16的数组
*/
public class HashMapDemo {
//以JDK7为例
public static void main(String[] args) {
HashMap map1=new HashMap();
//在实例化之后,底层创建一个长度为16的数组
map1.put("王祖贤",19);
/*
首先调用“王祖贤”(key)所在类的hashCode方法计算hash值
此hash值通过某种特定算法计算出该元素在数组中存放的位置
如果此位置上没有元素,则此时“王祖贤-19”添加成功
如果此位置上有元素,则比较“王祖贤”(key)的hash和已经存在的一个或者多个的key的hash
如果hash值完全不同,则此时“王祖贤-19”添加成功
如果hash值有相同的,则继续比较"王祖贤"与相同hash值的key的equals方法
如果equals方法返回false,则“王祖贤”与“相同hash值的key”不是同一个元素,则此时“王祖贤-19”添加成功
如果equals方法返回true,使用当前“王祖贤-19”的value去替换“相同hash值的key”的value
*/
map1.put("刘诗诗",20);
/*
HashMap扩容
当HashMap中的元素超过数组大小0.75(16*0.75=12)就扩容2倍
16-32-64....
*/
}
}
9.3.2TreeMap
/*
TreeMap
Map子接口 SortedMap;TreeMap实现了SortedMap。
底层使用红黑树结构存储数据
TreeMap存储key-value,需要根据key-value对进行排序
TreeMap可以保证所有key-value对处于有序状态
TreeMap的key排序
自然排序
定制排序
*/
public class TreeMapDemo {
public static void main(String[] args) {
TreeMap tm=new TreeMap();
tm.put(new User(),"语文");
}
}
class User implements Comparable{
int age;
String name;
@Override
public int compareTo(Object o) {
return 0;
}
}
十.IO流
/*
File普通方法
1.创建/删除
创建硬盘中对应的文件或者目录
boolean createNewFile() 创建文件,若文件存在,则不创建返回false;反之返回true
boolean mkdir() 创建文件目录,如果此文件目录的上层目录不存在,创建失败,返回false
boolean mkdirs() 创建文件目录,如果此文件目录的上层目录不存在,一并创建,返回true
删除硬盘中对相应文件或者目录
boolean delete();/删除文件或者文件目录
如果File对象的路径表示的是一个文件,那么直接删除该文件,返回true;反之返回false
如果File对象的路径表示的一个目录,那么判断目录下是否有其他文件或者文件夹,
如果有则删除失败,返回false
如果没有则删除成功,返回true
Java中删除不走回收站
*/
/*
2.获取
绝对路径:D:\\...(以磁盘名开始写的路径) C:\\.. F:\\..
相对路径:相较于某个路径下,指明的路径
获取绝对路径 String getAbsolutePath()
获取路径 String getPath()--获取的是创建File对象填入的路径
获取名字 String getName()
获取上层文件目录路径 String getParent()根据填入的path
获取文件长度(不能获取目录的长度) long length()
*/
/*
判断
boolean isFile() 判断文件是否存在
boolean isDirectory() 判断目录是否存在
boolean exists() 判断是否存在
*/
/*
String[] list();获取指定目录下所有文件或者目录的名称
File[] listFiles();获取指定目录下所有文件或者目录的File对象
*/
/*
删除File对象
1.判断File对象对应路径是否存在,不存在不删
2.若存在,判断File是目录还是文件
3.如果是文件则直接删除
4.如果是目录,则获取目录下所有的文件或者目录
5.遍历上述所有文件或者目录
6.判断File是文件还是目录
3456
递归:程序自己调用自己的一种编程技巧
*/
public class FileDemo05 {
public static void main(String[] args) {
File f=new File("D:\\vip06");
deleteFile(f);
}
public static void deleteFile(File e){
//1.判断路径是否存在,不存在方法直接结束
if(!e.exists()){//返回true表示存在,不存在返回false
return;
}
//2.判断是文件还是目录
//如果是文件,直接删除
if(e.isFile()){
e.delete();
}else if(e.isDirectory()){//如果是目录
//获取目录下所有内容
File[] fs=e.listFiles();
//遍历fs数组,依次调用当前的删除File方法
for(File f:fs){
deleteFile(f);
}
}
/*
当把目录下所有文件删除后,该目录也要删除
*/
e.delete();
}
}
10.1Buffered流
/*
处理流:程序通过一个间接流去调用节点流,以达到更加灵活方便的读写
各种类型的数据;这个间接的流就是处理流;缓冲流是一种处理流
Buffered流
字节 字符
输入 输出
缓冲字节输入流BufferedInputStream
缓冲字节输出流BufferedOutputStream
缓冲字符输入流BufferedReader
缓冲字符输出流BufferedWriter
缓冲流为了提高数据的读写速度
1.处理流建立在节点流之上
2.典型处理流叫缓冲流
3.典型节点流叫文件流
结论:缓冲流建立在文件流之上
*/
/*
案例:将d盘下BIS.mp4 复制到BIS_copy.mp4
*/
public class BufferedIODemo01 {
public static void main(String[] args) throws IOException {
//1.创建文件
File srcfile=new File("D:\\BIS.mp4");
File descfile=new File("D:\\BIS_copy.mp4");
//2.创建流(根据实际情况,选择缓冲字节流)
//2.1创建文件字节流
FileInputStream fis=new FileInputStream(srcfile);
FileOutputStream fos=new FileOutputStream(descfile);
//2.2创建缓冲字节流
//根据文件流创建缓冲流
BufferedInputStream bis=new BufferedInputStream(fis);//根据文件字节输入流创建缓冲字节输入流
BufferedOutputStream bos=new BufferedOutputStream(fos);//根据文件字节输出流创建缓冲字节输出流
//3.读写
byte[] bs=new byte[5];
int length;
long start=System.currentTimeMillis();
while ((length=bis.read(bs))!=-1){
bos.write(bs,0,length);
}
long end=System.currentTimeMillis();
System.out.println("复制花了:"+(end-start));
//4.关流
/*
关闭流的顺序和打开流的顺序相反
关闭流,关缓冲流,文件流也会关
*/
bis.close();
bos.close();
// fos.close();
// fis.close();
}
}
10.2流的分类
/*
IO流:
流的分类
按照数据类型:字节流/字符流
字节流:以字节单位进行,是通用的,可以操作任何文件
字符流:以字符单位进行,操作中字符方便(对记事本类似的文件进行操作)
按照数据流向:输入流(读)/输出流(写)
输入流:将文件中的内容输入(读取)到程序中
输出流:将程序中的内容输出(写出)到文件
字节输入流:InputStream
字节输出流:OutputStream
字符输入流:Reader(读)
字符输出流:Writer(写)
按照流的角色:节点流/处理流
节点流:程序用于直接操作目标设备所对应的流(类)叫节点流
如果目标设备是File
程序用于直接操作目标文件所对应的文件流叫节点流
File流
按照数据类型分: 文件字节流 文件字符流
按照流向分: 输入/输出 输入/输出
得到4种文件流:
文件字节输入流 文件字节输出流
FileInputStream FileOutputStream
文件字符输入流 文件字符输出流
FileReader FileWriter
处理流:程序通过一个间接流去调用节点流,以达到更加灵活方便的读写
各种类型的数据;这个间接的流就是处理流;缓冲流是一种处理流
Buffered流
*/
/*
案例:通过文件字符流,将demo.txt文件的内容复制到demo_copy.txt
*/
public class IOFile01 {
public static void main(String[] args) throws IOException {
//1.创建文件(根据实际例子创建File对象)
File srcFile=new File("D:\\demo.txt");
File descFile=new File("D:\\demo_copy.txt");
//2.创建流(根据实际情况选择何时流,)
//根据实际情况,在这里是操作记事本中的内容(有可能是中文的).所以选择字符流
//具体选择 文件字符流
FileReader reader=new FileReader(srcFile);//根据源文件创建输入流(是对源文件进行输入)
FileWriter writer=new FileWriter(descFile);//根据目标文件创建输出流(是对目标文件进行输出)
//3.数据的读写
/* int read()返回读入一个字符,如果达到文件末尾,返回-1
void write(int c) 向文件中写入一个字符
int data;//存储读到的内容
while((data=reader.read())!=-1){//读到data不等于-1的时候
writer.write(data);//调用写方法进行写
}
*/
/*
int read(char[] chs);返回每次输入数组中的字符个数,如果达到文件末尾,返回-1
void write(char[] chs,int off,int length)写入字符数组chs的一部分 ,off从那个位置开始,length要写多长
*/
char[] chs=new char[10];
int length;
while((length=reader.read(chs))!=-1){
writer.write(chs,0,length);
}
//4.关流(虽然不影响程序运行,但是必须得关)
reader.close();
writer.close();
}
}
10.3对象流
/*
对象流:ObjectInputStream ObjectOutputStream
用于存储和读取基本类型数据和对象的处理流 (建立再节点流之上)
java对象写入到数据源,也能把对象从数据源中还原回来
序列化:java对象写入到数据源(文件)中;用oos保存对象的机制
反序列化:把对象从数据源(文件)中还原回来;用ois读取对象的机制
*/
序列化:
public class ObjectDemo01 {
/*
序列化:java程序---文件
*/
public static void main(String[] args) throws IOException {
// //1.创建文件
// File f=new File("D:\\demo.txt");
// //2.创建流
// //2.1创建文件字节输出流
// FileOutputStream fos=new FileOutputStream(f);
// //2.2创建对象流
// ObjectOutputStream oos=new ObjectOutputStream(fos);
ObjectOutputStream oos=new ObjectOutputStream(
new FileOutputStream(
new File("D:\\demo.txt")));
//3.写操作
oos.writeObject(new Date());
oos.writeObject(new Student());//改行出错
oos.writeObject(new Integer("10"));
oos.writeObject(new Boolean("true"));
oos.writeObject(new Double("4.3"));
oos.writeObject(null);
//4.关流
oos.close();
}
}
class Student implements Serializable {
/*
自定义类型如果要实现序列化和反序列化,必须实现Serializable结构
*/
}
反序列化:
/*
反序列化:文件-Java程序
*/
public class ObjectDemo02 {
public static void main(String[] args) throws Exception {
//1创建文件
//2创建流
//2.1创建文件字节输入流
//2.2创建对象出入流
ObjectInputStream ois=new ObjectInputStream(
new FileInputStream(
new File("D:\\demo.txt")));
//3读
// Object o1= ois.readObject();
// Object o2= ois.readObject();
// Object o3= ois.readObject();
// Object o4= ois.readObject();
// Object o5= ois.readObject();
// System.out.println(o1+"\n"+o2+"\n"+o3+"\n"+o4+"\n"+o5);
Object obj;
while((obj=ois.readObject())!=null){
System.out.println(obj);
}
//4.关流
ois.close();
}
}
10.4随机流
/*
1.随机流
2.对象流
3.转换流
*/
public class RandomDemo01 {
/*
随机流:支持随机访问,程序可以直接跳到文件的任意地方来读/写
支持访问文件的部分内容
可以向已经存在的文件后追加内容
RandomAccessFile对象包含一个记录指针,用来表示当前读写的位置
RandomAccessFile对象可以自由移动记录指针
void seek(long pos)将文件记录指针的位置设置到pos位置
long getFilePointer()获取文件记录指针的位置
构造器:
RandomAccessFile(File file,String mode)
RandomAccessFile(String name,String mode)
mode:
r:以只读方式打开
rw:以读写方式打开
rwd:以读写方式打开;同步文件内容的更新
rws:以读写方式打开;同步文件内容和元数据的更新
案例:假设demo.txt文件中123456789,需要4 和5中间写个0进去
*/
public static void main(String[] args) throws Exception {
//1.创建文件
File f=new File("D:\\demo.txt");
//2.创建流
RandomAccessFile raf=new RandomAccessFile(f,"rw");
// RandomAccessFile raf=new RandomAccessFile(new File("D:\\demo.txt"),"rw");
//第一步和第二步一起操作
// RandomAccessFile raf1=new RandomAccessFile("D:\\demo.txt","rw");
//3.随机读写
/*
3.1将记录指针移动到4的位置
3.2读取4以后的内容,将读取到的内容存储起来(记录指针已经指在文件末尾)
3.3将记录指针再次移动4的位置
3.4将0写入
3.5将第2步存储的内容再次写入到文件中
*/
// 3.1将记录指针移动到4的位置
raf.seek(4);
// 3.2读取4以后的内容,将读取到的内容存储起来(记录指针已经指在文件末尾)
//3.2.1创建StringBuilder对象用来存储读到的内容
StringBuilder sb=new StringBuilder();
//3.2.2读取
byte[] bs=new byte[6];
int length;
while((length=raf.read(bs))!=-1){
//3.2.3读取将读取到内容写到sb中
//bs{5,6,7,8,9}
sb.append(new String(bs,0,length));//将bs数组中从0开始,取length长,转为字符串
}
// 3.3将记录指针再次移动4的位置
raf.seek(4);
// 3.4将0这个字符写入到文件中
raf.write('0');
// 3.5将第2步存储的内容再次写入到文件中
//3.5.1先将StringBuilder转为String
String str=sb.toString();
//3.5.2再将String转为字节数组
byte[] str_bs=str.getBytes();
//3.5.3将上述字节数组写入到文件即可
raf.write(str_bs);
//4.关闭流
raf.close();
}
}
10.5转换流
/*
1.转换流
转换流可以实现字节流和字符流的转换
InputStreamReader:
将字节输入流转成字符输入流
需要和InputStream套接使用
InputStreamReader(InputStream in)
InputStreamReader(InputStream in,String charset)
OutputStreamWriter:
将字符输出流转成字节输出流
需要和OutputStream套接使用
OutputStreamWriter(OutputStream out)
OutputStreamWriter(OutputStream out,String charset)
字符到字节:编码 字节到字符:解码
2.数据流
3.打印流
*/
public class IODemo01 {
public static void main(String[] args) throws Exception {
//1.创建文件
//2.创建流
InputStreamReader isr=new InputStreamReader(
new FileInputStream(
new File("D:\\demo.txt")),"gbk");
OutputStreamWriter osw=new OutputStreamWriter(
new FileOutputStream(
new File("D:\\demo_copy.txt")),"utf-8");
//3.读写
char[] chs=new char[10];
int length;
while((length=isr.read(chs))!=-1){
osw.write(chs,0,length);
}
//4.关流
isr.close();
osw.close();
}
}
10.6数据流
/*
2.数据流
可以更方便地操作java基本数据类型和String的流,数据流也是一种处理流
DataInputStream DataOutputStream
*/
public class IODemo02 {
public static void main(String[] args) throws IOException {
//1.创建文件
//2.创建流
DataOutputStream dos=new DataOutputStream(
new FileOutputStream(
new File("D:\\dd.txt")));
//3.写
StringBuilder sb=new StringBuilder();
sb.append(3.14);
sb.append(true);
sb.append('C');
//4.关流
dos.close();
//1.创建文件
//2.创建流
DataInputStream dis=new DataInputStream(
new FileInputStream(
new File("D:\\dd.txt")));
//3.读
//读取不同类型的数据的顺序,要与当初存储的顺序保持一致
double d=dis.readDouble();
System.out.println(d);
char c=dis.readChar();
System.out.println(c);
boolean b=dis.readBoolean();
System.out.println(b);
//4.关流
dis.close();
}
}
10.7打印流
/*
3.打印流
PrintWriter是一个高级流
1.可以按行写出字符串
2.可以自动刷新
*/
public class IODemo03 {
public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
// //1创建文件
// File f=new File("D:\\dd.txt");
// //2创建流
// //2.1.节点流(操作字节)
// FileOutputStream fos=new FileOutputStream(f);
// //2.2.处理流(转换流)(操作字符)
// OutputStreamWriter osw=new OutputStreamWriter(fos,"utf-8");
// //2.3.处理流(缓冲流)(提高效率)
// BufferedWriter bw=new BufferedWriter(osw);
// //2.4.打印流(自动行刷新)
// //PrintWriter(Writer,boolean)boolean表示的是否自动行刷新
// PrintWriter pw=new PrintWriter(bw,true);
PrintWriter pw=new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(
new File("D:\\dd.txt")),
"utf-8")),
true);
//3.写
System.out.println("请输入你要打印到文件中的内容");
Scanner scan=new Scanner(System.in);
while(true){//循环接收键盘输入的内容
String line=scan.nextLine();
if("exit".equals(line)){//如果输入是退出,则循环就结束
break;
}
//pw.println(line);//如果输入的不是退出,则将字符串写入到文件中
pw.print(line);
pw.flush();//手动刷新
}
System.out.println("输入结束 !!");
//4.关流
pw.close();
}
}
十一.线程
11.1认识线程
/*
程序:为了完成特定任务,用某种编程语言编写的一组指令,所以程序就是一段静态的代码
进程:程序的一次执行过程,是一个动态的过程 ;生命周期:产生-- 存在 --- 消亡
线程:进程进一步细化为线程,一个程序内部的一条执行路径
什么时候需要多线程?
1.程序需要同时执行多个任务的时候
2.程序需要实现一些需要等待的任务时
3.需要执行一些后台运行程序时,
多线程有优点?
1.提高应用程序的响应
2.提高计算机系统cpu的利用率
3.改善程序的结构
*/
public class Demo01 {
public static void main(String[] args) {
// 3.创建线程对象/实例(根据实际情况创建一个或多个对象/实例)
TestDemo td1=new TestDemo();
TestDemo td2=new TestDemo();
TestDemo td3=new TestDemo();
// 4.启动线程
/*
1.启动线程调用的是Thread的start()方法
2.启动线程后自动调用run();
所以,需要线程完成某件事儿,只需将对应代码放入run方法中即可
*/
td1.start();//Thread-0
td2.start();//Thread-1
td3.start();//Thread-2
}
}
11.2如何实现多线程
方式一:
/*
java如何实现多线程
方式一:继承Thread
Thread:在java中用来表示线程的类,里面包含一个线程运行过程的方法run()
在执行线程的时候,需要执行什么代码否放到run方法中即可
1.写一个类,继承Thread
2.在该类中重写run方法,将需要线程执行的代码放入run方法中
3.创建线程对象/实例(根据实际情况创建一个或多个对象/实例)
4.启动线程
*/
// 1.写一个类,继承Thread
public class TestDemo extends Thread{
int num=1;
@Override
public void run() {
for (int i=1;i<100;i++){
System.out.println(this.getName()+":雅蠛蝶!我是"+num+++"号技师~");
}
}
}
方式二:
/*
java如何实现多线程
方式二:Runnable接口
1.写一个类,实现Runnable接口
2.在该类中重写run方法
3.根据该类创建对象
4.需要将该对象传入到Thread构造器中
5.调用Thread的start方法
总结:
1.继承Thread不能实现共享数据,当实现Runnable后,代码可以被多个线程共享,增加程序的强壮性
2.java只支持类的单一继承,当继承Thread之后不能再继承其他类,但是实现Runnable可以再实现其他接口,可以继承其他类
3.Runnable适合多个相同的程序代码的线程去处理同一个资源
4.线程池只能放实现Runnable接口的线程
Runnable优势较大~
*/
public class ThreadDemo02 {
public static void main(String[] args) {
//3.根据该类创建对象
TesRunnable tr=new TesRunnable();
// 4.需要将该对象传入到Thread构造器中
Thread t1=new Thread(tr);
Thread t2=new Thread(tr);
Thread t3=new Thread(tr);
// 5.调用Thread的start方法
t1.start();
t2.start();
t3.start();
}
}
// 1.写一个类,实现Runnable接口
class TesRunnable implements Runnable{
int num=1;
// 2.在该类中重写run方法
@Override
public void run() {
for (int i=0;i<10;i++){
//currentThread()获取当前线程对象;getName获取当前线程的名字
System.out.println(Thread.currentThread().getName()+":亚麻跌,我是"+num+++"号,技师");
}
}
}
方式三:
/*
第二种方式的变种:匿名内部类方式
*/
public class ThreadDemo03 {
public static void main(String[] args) {
Boo b=new Boo();//Boo是一个类
Aoo a=new Aoo(){
@Override
public void run() {
}
};
Thread t1=new Thread(){
@Override
public void run() {
for (int i=0;i<10;i++){
System.out.println("德玛西亚!!!");
}
}
};
Thread t2=new Thread(){
@Override
public void run() {
for (int i=0;i<10;i++){
System.out.println("EDG牛掰!!");
}
}
};
t1.start();
t2.start();
}
}
class Boo{
}
interface Aoo{
void run();
}
11.3线程生命周期
/*
线程的生命周期
1.新建状态(new)
当使用new关键字创建线程实例后,该线程处于新建状态,但是不会执行
例如:Thread t=new Thread()
2.就绪状态(Runnable)
当调用start方时,该线程处于就绪状态,表示可以执行,但是不一定会
立即执行,而是等待cpu分配时间片处理
例如:t.start()
3.运行状态(Running)
当cpu为该线程分配到时间片,执行该线程的run()方法时,就处于运行
状态;时间片结束,线程该没执行完时,线程就变成就绪状态,继续等待cpu
分配时间片
4.暂停状态(Block)
当线程调用sleep/wait/等待键盘输入(scanner),主动放弃cpu资源
当sleep休眠时间结束/wait被唤醒/键盘接收完毕,线程就又回到就绪状态,
等待cpu分配时间片
5.死亡状态(Dead)
当线程的run方法执行完毕,程序出现异常,线程就处于死亡状态
注意:
1.当线程创建时候,不会立即执行,需要调用start方法,使其处于就绪状态,
2.线程处于就绪状态时,也不会立即执行,需要等待cpu分配时间
3.当线程处于阻塞(暂停)状态,会让出占有cpu,当阻塞(暂停)结束时,线程就会进入就绪
状态,重新等待cpu,而不是直接进入运行状态
*/
11.4操作线程的方法
11.4.1线程休眠
/*
操作线程常见的方法:
1.线程休眠
static void sleep(long)使当前的线程休眠指定毫秒值
*/
public class ThreadDemo01 {
public static void main(String[] args) throws InterruptedException {
for(int i=10;i>0;i--){
Thread.sleep(1000);
System.out.println(i);
}
}
}
11.4.2线程中断
/*
2.线程中断方法
interrupt();将线程中断(将当前线程改成中断状态,)
isInterrupt();判断线程是否是中断状态
*/
public class ThreadDemo02 {
public static void main(String[] args) {
ThreadDemo td=new ThreadDemo();
td.start();
}
}
class ThreadDemo extends Thread{
@Override
public void run() {
for(int i=0;i<100;i++){
try {
/*
sleep方法中处理中断异常的操作
当线程调用sleep方法后处于阻塞状态,
此时如果调用该线程的中断方法,那么sleep
会立即抛出一个中断异常,会中断当前阻塞状态
而且,中断状态会被清除
*/
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(i==5){//当i=5
interrupt();//调用线程中断方法
}
System.out.println(i+"线程的中断状态:"+isInterrupted());
}
}
}
11.4.3设置线程优先级
/*
3.线程的优先级别
设置线程优先级 setPriority(int )
1-10: 1最小优先级 5默认优先级 10最大优先级
优先级越高,获得cpu的机会就越多,并不代表一定是先执行
*/
public class ThreadDemo03 {
public static void main(String[] args) {
TD td1=new TD();
td1.setPriority(5);
TD td2=new TD();
td2.setPriority(10);
td1.start();
td2.start();
}
}
class TD extends Thread{
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(getName()+":执行"+i);
}
}
}
11.4.4获取当前线程
/*
4.获取当前线程 static Thread currentThread()
getName()获取当前线程的名字
setName()设置当前线程的名字
isAlive()判断当前线程是否是活着
*/
public class ThreadDemo04 {
public static void main(String[] args) {
//获取当前正在运行的线程---main线程
Thread t= Thread.currentThread();
System.out.println(t);//Thread[main,5,main] 默认优先级5
t.setName("成博");
System.out.println(t.getName());
System.out.println(t.isAlive());
}
}
11.4.5让出当前cpu时间片
/*
5.让出当前cpu时间片:与其他线程还是公平等待分配cpu
static void yield()
*/
public class ThreadDemo05 {
public static void main(String[] args) {
yieldTest yt1=new yieldTest();
yieldTest yt2=new yieldTest();
yt1.start();
yt2.start();
}
}
class yieldTest extends Thread{
@Override
public void run() {
for(int i=0;i<100;i++){
if(i%10==0){
yield();
}
System.out.println(getName()+":"+i);
}
}
}
11.4.6等待当前线程结束
/*
6.用于等待当前线程结束 join()
//先下载好图片,再显示图片
*/
public class ThreadDemo06 {
public static boolean isFinish=false;//用于表示图片是否下载完毕
public static void main(String[] args) {
//图片下载
Thread download=new Thread(){
@Override
public void run() {
System.out.println("down:图片开始下载.....");
for (int i=1;i<=100;i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//设置线程休眠,每隔一百毫秒下载一个点
System.out.println("down:下载进度"+i+"%");
}
System.out.println("down:图片下载完毕");
isFinish=true;
}
};
download.start();
//图片显示
Thread show=new Thread(){
@Override
public void run() {
System.out.println("show:图片开始显示....");
//等待download执行结束,等待图片下载完毕
try {
download.join();//使用join方法,等待download执行结束
if(isFinish){//true
System.out.println("show:图片显示完毕");
}else{
System.out.println("show:图片显示失败,未成功下载");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
show.start();
}
}
11.4.7设置守护线程
/*
7.设置守护线程
守护线程也就是后台线程,默认创建都是普通线程
守护线程是通过setDaemon()方法把普通线程变为守护线程
守护线程使用上与普通线程是没有区别的,但是结束时机上
有一点不同,当一个进程中所有的普通线程都结束时,进程会
结束,此时正在运行的所有守护线程会强制杀掉
*/
public class ThreadDemo07 {
public static void main(String[] args) {
Thread ros=new Thread(){
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println("ros:jack,我要跳了~");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("ros:jack,你快来守护我啊!");
System.out.println("ros:扑通!");
}
};
Thread ros2=new Thread(){
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println("ros:jack,我要跳了~");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("ros:jack,你快来守护我啊!");
System.out.println("ros:扑通!");
}
};
Thread jack=new Thread(){
@Override
public void run() {
while(true){
System.out.println("jack:ros,我永远守护着你,陪伴着你!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
//将jack设置为守护线程
jack.setDaemon(true);
ros.start();
ros2.start();
jack.start();
}
}
11.5线程安全
/*
线程安全
三个窗口卖票,总数100张,使用Runnable接口实现
1.卖票过程中出现重票,错票----线程安全问题
2.出现问题的原因:当某个线程操作车票的过程中,其他线程参与进来了,也在操作车票
3.如何解决:当一个线程在操作车票的时候,其他线程不能参与进来,直到当前线程操作结束
才允许其他线程进入(加锁)
4.在java中,如何解决线程安全问题
方法一:同步代码块
synchronize(同步监视器){
//需要被同步代码块
}
说明:1.同步监视器,俗称锁,任何一个类的对象都可以来充当锁(多线程必须要公用一把锁)
2.需要被同步代码块,操作共享数据的代码
3.共享数据:多个线程共同操作的变量--票数
方法二:同步方法
定义一个方法用synchronize修饰
将操作共享数据代码放入到这个方法即可
*/
public class ThreadDemo08 {
public static void main(String[] args) {
Ticket t=new Ticket();
Thread t1=new Thread(t,"窗口1");
Thread t2=new Thread(t,"窗口2");
Thread t3=new Thread(t,"窗口3");
t1.start();
t2.start();
t3.start();
}
}
class Ticket implements Runnable{
int ticket=100;
@Override
public void run() {
while(true){
show();
}
}
public synchronized void show(){
if (ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":抢到的票号为:"+ ticket--);
}
}
}
11.6死锁
/*
死锁:不同线程分别占用对方需要的同步资源不放弃
都在等待对方放弃自己需要的同步资源,就形成线程的死锁
注意: 出现死锁后,不会出现异常,不含出现提示,只是所有的
线程都处于阻塞状态无法继续
我们使用同步时,需要避免出现死锁
解决方式:
尽量减少同步资源的定义
尽量避免同步的嵌套
专门的算法/原则
*/
public class ThreadDemo02 {
public static void main(String[] args) {
StringBuilder sb1=new StringBuilder();
StringBuilder sb2=new StringBuilder();
new Thread(){
@Override
public void run() {
synchronized (sb1){
sb1.append("a");
sb2.append("1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (sb2){
sb1.append("b");
sb2.append("2");
System.out.println(this.getName()+":"+sb1);
System.out.println(this.getName()+":"+sb2);
}
}
}
}.start();
new Thread(){
@Override
public void run() {
synchronized (sb2){
sb1.append("c");
sb2.append("3");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (sb1){
sb1.append("d");
sb2.append("4");
System.out.println(this.getName()+":"+sb1);
System.out.println(this.getName()+":"+sb2);
}
}
}
}.start();
}
}
11.7线程通信
/*
线程通信
例如:A B C 用三个线程交叉输出5个数
A:12345 B:678910 C:11 12 13 14 15 。。。
1. wait();一旦执行此方法,当前线程就进入阻塞状态,并且释放同步监视器
2. notify();一旦执行此方法,就会唤醒被wait阻塞的一个线程;如果有多个线程被wait阻塞了,会优先唤醒优先级高的线程
3. notifyAll();一旦执行此方法,就会唤醒被wait阻塞的所有线程,
说明:
1.上述三个方法必须使用在同步代码块或同步方法中
2.上述三个方法定义在java.lang.Object中
3.上述三个方法调用者必须是同步代码块或者同步资源中同步监视器
sleep()和wait的异同
1.相同点:都可以使当前线程进入阻塞状态
2.不同点:
a.两个方法的声明位置不同:Thread中声明sleep,Object中声明wait
b.调用要求不同:sleep任何地方都可以使用,wait必须在同步代码块中或者方法中调用
c.释放同步监视器:如果两个方法都在同步代码块或者同步资源中调用
sleep不会释放同步资源,wait会释放同步资源
*/
public class ThreadDemo03 {
public static void main(String[] args) {
numTest nt=new numTest();
Thread t1=new Thread(nt,"A");
Thread t2=new Thread(nt,"B");
Thread t3=new Thread(nt,"C");
t1.start();
t2.start();
t3.start();
}
}
class numTest implements Runnable{
int i=0;//计数器
@Override
public void run() {
while(true){
show();
}
}
public synchronized void show(){
//每次执行的时候,需要唤醒被wait的线程
notify();
i++;
System.out.println(Thread.currentThread().getName()+":"+i);
if(i%5==0){//i%5==0的时候你让当前的线程进入阻塞状态,换其他线程
try {
wait();//当前线程进入阻塞状态,释放同步方法使用权
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
11.8生产者消费者问题
/*
生产者消费者问题
生产者生产好产品交给店员,消费者从店员手中取走商品
店员一次只能只有固定数量的产品(20),如果生产者试图生产更多的产品,
店员会叫生产者等待一下,如果店员手中有空位置,在通知生产者,继续生产
如果店员手中没有产品了,店员告诉消费者要等一下,如果店员手中有了产品
再通知消费者来领走产品
问?
1.是否有多线程问题?是,生产者线程,消费者线程
2.是否有数据共享?是,产品数量,店员
3.是否存在线程安全问题?如何解决 同步?
*/
public class ThreadDemo04 {
public static void main(String[] args) {
//创建店员对象
Clerk c=new Clerk();
// 创建生产者线程
Productor dls=new Productor(c);
dls.setName("杜蕾斯");
Productor jsb=new Productor(c);
jsb.setName("杰士邦");
//创建消费者线程
Consumer cb=new Consumer(c);
cb.setName("成博");
Consumer ss=new Consumer(c);
ss.setName("山山");
jsb.start();
dls.start();
cb.start();
ss.start();
}
}
//店员类
class Clerk{
//产品数量
int productorCount=0;
//店员要求生产产品
public synchronized void produceProductor(){
if(productorCount<20){//当数量小于20的需要唤醒被等待的线程
notify();//
productorCount++;
System.out.println(Thread.currentThread().getName()+":开始生产第"+productorCount+"个产品!");
}else{//大于等于20,不生产,调用wait方法使线程进入等待状态
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//店员的消费商品
public synchronized void consumerProductor(){
if (productorCount>0){//产品数量大于0的时候可以消费
notify();//唤醒被阻塞线程
System.out.println(Thread.currentThread().getName()+":开始消费第"+productorCount+"个产品!");
productorCount--;
}else {
try {
wait();//当产品数量小于0的时候,改线程进入阻塞状态
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//生产者线程
class Productor extends Thread{
Clerk clerk;
public Productor(Clerk clerk){
this.clerk=clerk;
}
@Override
public void run() {
System.out.println(getName()+":开始生产产品......");
while(true){//循环调用生产产品方法
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.produceProductor();
}
}
}
//消费者线程
class Consumer extends Thread{
Clerk clerk;
public Consumer(Clerk clerk){
this.clerk=clerk;
}
@Override
public void run() {
while(true){
System.out.println(getName()+":正在消费产品......");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.consumerProductor();
}
}
}
11.9实现多线程的方法
/*
javaj实现多线程的方法一
继承Thread
java实现多线程的方法二
实现Runnable接口
重写run方法
java实现多线程的方法三
实现Callable接口
重写Call接口
FutureTask可以封装Runnable/Callable;
该类也实现Runnable
将Callable实现类对象封装到FutureTask对象中
然后将该对象传入Thread中
实现Callable方式和Runnable方式的优势
1.Call可以有返回值的
2.Call可以抛出异常,被外面的操作捕获,获取更多的异常信息
3.Call是支持泛型的
*/
public class ThreadDemo01 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//3.创建Callable实现类对象
CallThread ct=new CallThread();
//4.将Callable实现类对象封装到FutureTask对象中
FutureTask ft=new FutureTask(ct);
//5.将FutureTask对象封装到Thread对象中
Thread t=new Thread(ft);
//6.启动线程
t.start();
/*
在call中的返回值是FutureTask对象调用get方法获取
*/
//7.可以通过get方法得到call中的返回值
System.out.println(ft.get());
}
}
//1.创建一个类实现Callable接口
class CallThread implements Callable{
//2.重写call方法
@Override
public Object call() throws Exception {
int sum=0;
for(int i=1;i<=100;i++){
sum+=i;
}
return sum;
}
}
线程池:
/*
java实现多线程的方法四
线程池
背景:
经常创建和销毁、使用量特别大的资源,对计算机使用性能上影响很大,
思路:
提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中
可以避免频繁创建、销毁,实现重复利用,类似于共享单车
好处:
提高响应速度(减少了创建新线程的时间)
降低资源消耗(重复利用线程池中线程)
便于线程管理
核心池的大小
最大线程数
线程没有任务时最多保持多长时间后终止
java中线程池
Array--表示数组 Arrays--表示数组工具类
Executor-表示线程池 Executors-表示线程池工具类
JDK5起提供了线程池相关API:ExecutorService和Executors
ExecutorService:真正的线程池接口,常见实现类ThreadPollExecutor
void execute(Runnable r);执行任务/命令,没有返回值,一般用于执行Runnable
Future submit(Callable c);执行任务,有返回值,一般用来执行Callable
void shutdown();关闭连接
不会立即终止线程,而是等待所有任务列表中任务执行完之后才终止
不会接收新的任务
void shutdownNow();关闭连接
立即终止线程,并且尝试打断正在执行的任务,清空任务列表中尚未执行的任务
Executor:线程池接口
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
创建四种线程池
1. ExecutorService newFixedThreadPool(6)
创建一个固定长度的线程池
2. ExecutorService newCachedThreadPool()
创建可以缓存的线程,可以根据需要创建新线程的线程池,会回收空的线程
3.ExecutorService newSingleThreadExecutor()
创建一个单线程;只有一个线程的线程池
4. ScheduledExecutorService newScheduledThreadPool()
创建一个固定长度的线程池 ,而且以延迟或者定时的方式执行
*/
public class ThreadDemo02 {
public static void main(String[] args) {
// test01();
// test02();
//test03();
test04();
}
//1.创建一个固定长度的线程池,当达到线程池最大数量,线程池的规模是不会再变化
public static void test01(){
//a.根据线程池工具类调用方法创建固定长度的线程池
ExecutorService service= Executors.newFixedThreadPool(6);
//b.启动10个线程
for(int i=0;i<10;i++){
service.execute(new TicketOut());
}
//c.关闭线程
service.shutdown();
//System.out.println("线程池运行结束....");
}
//2.创建可以缓存的线程,可以根据需要创建新线程的线程池,会回收空的线程
public static void test02(){
//a.根据线程池工具类调用方法创建可缓存线程池
ExecutorService service= Executors.newCachedThreadPool();
//b.启动10个线程
for(int i=0;i<15;i++){
service.execute(new TicketOut());
}
//c.关闭线程
service.shutdown();
}
//3.创建一个单线程;只有一个线程的线程池
//
public static void test03(){
//a根据线程池工具类调用方法创建单线程的线程池
ExecutorService service= Executors.newSingleThreadExecutor();
//b启动10个线程
for(int i=0;i<10;i++){
service.execute(new TicketOut());
}
//c关闭线程
service.shutdown();
}
//4.创建一个固定长度的线程池,而且以延迟或者定时的方式执行
public static void test04(){
ScheduledExecutorService service=
Executors.newScheduledThreadPool(6);
for (int i=0;i<10;i++){
/*
定时
TimeUnit.SECONDS一秒
*/
service.schedule(new TicketOut(),1, TimeUnit.SECONDS);
}
service.shutdown();
}
}
class TicketOut implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在霸占抢票窗口...卖票中...");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢票结束...溜了溜了....");
}
}
十二.网络编程
12.1IP
/*
网络编程:
计算网络
把分布在不同地理区域的计算机与专门外部设备用通信线路互连成一个规模大
功能强的网络系统,从而使多个计算机方便进信息传递/共享硬件/软件/数据等.
java实现网络编程主要问题:
1.如何准确定位到网络上的一台或者多台主机上特定的应用
2.找到主机后如何可靠高效地进行数据的传递
java网络编程的两个因素
1.IP和端口
2.网络通信协议
IP和端口
IP的理解:在Internet上计算机的唯一标识
IP地址分为IPv4 IPv6
IPv4 :4个字节组成,0-(2^8-1);0-255;42亿个IP(70亿*4)
30亿都在北美,4亿亚洲
2011年初已经用完;192.168.1.1
IPv6:16个字节组成,128位,写成8个无符号的整数,每个整数用4个十六进制表示
3ad4:4ffa:3ad4:4ffa:3ad4:4ffa:3ad4:4ffa
IP地址分为:公网地址(万维网使用)和私有地址(局域网)
192.168.0.0-192.168.255.255
IP地址在Internet上两种表现形式
域名(hostName):www.baidu.com
IP地址(hostAddress):14.215.177.38
当在连接网络时输入一个主机的域名后,域名服务器(DNS)
负责将域名转换为IP地址,这样才能和主机建立连接----域名解析
域名容易记
java中表示ip地址的类
InetAddress类主要表示IP地址
两个子类 Inet4Address Inet6Address
*/
public class IPDemo01 {
/*
java提高ip的封装类InetAddress,这个类位于java.net包
主要封装ip地址,并且提供 相关方法,获取IP地址主机地址
*/
public static void main(String[] args) throws UnknownHostException {
/*
静态方法获取地址对象
static InetAddress getLocalHost();获取本地主机地址对象
static InetAddress getByName(String hostName);根据指定域名获取指定主机地址对象
*/
//获取本地主机地址对象
InetAddress ia1=InetAddress.getLocalHost();
System.out.println(ia1);
//获取指定域名主机地址对象
InetAddress ia2=InetAddress.getByName("www.baidu.com");
System.out.println(ia2);//www.baidu.com/14.215.177.38
/*
常用方法获取地址对象中的信息
获取主机名 getHostName()
获取主机ip地址 getHostAddress()
*/
System.out.println("ia1的主机名:"+ia1.getHostName());
System.out.println("ia1的IP地址:"+ia1.getHostAddress());
/*
当遇到ip地址不可能只有一个情况,如何获取所有ip地址
static InetAddress getAllByName(String hostname)
*/
InetAddress[] is=InetAddress.getAllByName("www.baidu.com");
for (InetAddress i:is){
System.out.println(i.getHostAddress());
}
}
}
12.2Socket
/*
端口号:
标识正在计算机上运行的进程(线程)
不同的进程有不同的端口号
被规定为一个16位的无符号整数0-65535
端口分类
公认端口:0-1023,被预先定义的服务通信占用(如:HTTP占用端口80,FTP占用端口21.Telnet占用端口23)
注册端口:1024-49151,分配给用户进程或应用程序(如:Tomcat占用端口8080,MySQL占用3306,Oracle占用1521等)
动态/私有端口:49152-65535,
端口号和IP地址的组合得出一个网络套接字:Socket
网络通信协议:
计算机网络中实现通信必须有一些约定,即通信协议对速率/传输代码/代码结构/传输控制步骤/出错控制等制定标准
有两套参考模型
OSI参考模型:模型过于理想化,没有能因特网上进行广泛的推广
TCP参考模型:国际标准
TCP网络通信协议
使用TCP协议前,必须先建立TCP连接,形成传输数据通道
传输前,采用"三次握手"方式,点对点通信,是非常可靠
在连接中可以进行大数据传输,数据传输完毕后,需要释放连接,效率低
UDP协议
将数据/源/目标封装成数据包,不需要建立连接
发送方不管对象是否准备好,接收方收到也不会确认,不可靠
发送数据结束时,无需释放资源,开销小,速度快
每个数据包的大小限制在64k内
Java的TCP通信编程
Socket(套接字)
网络上具有唯一标识的ip地址和端口号组合在一起构成的唯一识别的标识符套接字
Socket原理机制
通信的两端(客户端/服务端)都有socket
网络通信其实就是Socket通信
数据在两个Socket间通过IO通信
客户端
输入:服务端发送过来的数据
输出:客户端要给服务端发送的数据
服务端
输入:客户端发送过来的数据
输出:服务端要给客户端发送的数据
*/
12.3案例
12.3.1
/*
创建Socket对象,指明需要连接的服务器的地址和端口号
建立连接后,通过输出流向服务器端发送请求信息
通过输入流获取服务器响应的信息
*/
public class ClientDemo {
public static void main(String[] args) throws IOException {
ClientDemo client=new ClientDemo();
client.action();
}
/*
启动客户端
*/
public void action() throws IOException {
//1.创建Socket
Socket socket=new Socket("localhost",8089);
//2.获取输出流输入流
OutputStream os=socket.getOutputStream();
InputStream is=socket.getInputStream();
//防止阻塞,使用多线程,一个线程读,一个线程写
System.out.println("请输入客户端名字:");
Scanner scanner=new Scanner(System.in);
String name=scanner.nextLine();
//创建读线程,并启动
new ReadFromServer(name,is).start();
//创建写线程,并启动
new WriteToServer(name,os).start();
}
}
/*
从服务器读取消息的线程
*/
class ReadFromServer extends Thread{
String name;
InputStream is;//封装了服务器发送过来的信息流
public ReadFromServer(String name, InputStream is) {
this.name = name;
this.is = is;
}
@Override
public void run() {
try {
//根据从Socket中获取的InputStream套娃得到一个BufferedReader
BufferedReader br=new BufferedReader(new InputStreamReader(is,"utf-8"));
String line=null;
while((line=br.readLine())!=null){
//循环读取,读到不为空的内容的输出,继续读
System.out.println(line);
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class WriteToServer extends Thread{
String name;
OutputStream os;
public WriteToServer(String name, OutputStream os) {
this.name = name;
this.os = os;
}
@Override
public void run(){
try {
//创建键盘扫描器
Scanner scan=new Scanner(System.in);
//根据Socket中OutputStream得到一个PrintWriter,做输出
PrintWriter pw=new PrintWriter(new OutputStreamWriter(os,"utf-8"),true);
//先输出自己用户名
pw.println(name);
while(true){
//循环接收键盘输入的内容,
String line=scan.nextLine();
//循环输出到服务器端
pw.println(name+"说:"+line);
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
12.3.2
/*
一个服务端和两个客户端聊天
思路:使用多线程的方式
服务器端创建ServerSocket,循环调用accept()等待客户端连接
客户端创建一个Socket并请求和服务器端连接
服务器端接收请求,创建Socket与该客户端建专线连接
建立连接后的两个Socket在一个单独的线程上对话
服务器端继续等待客户端
*/
public class ServerDemo {
//客户信息(用于存储客户名以及对应输出流)
Map<String, PrintWriter> map=new HashMap<>();
public static void main(String[] args) throws IOException {
//创建服务端对象
ServerDemo server=new ServerDemo();
//调用服务端启动方法
server.action();
}
/*
服务端启动方法
*/
public void action() throws IOException {
//1.创建服务器
ServerSocket server=new ServerSocket(8089);
//2.循环等待客户端连接
System.out.println("qq群聊功能已开启,等待用户进入群聊.....");
while(true){
Socket socket=server.accept();
//3.给每个客户端单独开辟一个线程
new Thread(new ClientThread(socket,map)).start();
}
}
}
class ClientThread implements Runnable{
Socket client;//与客户端连接的socket,获取输入输出流
Map<String, PrintWriter> map;
public ClientThread(Socket client, Map<String, PrintWriter> map) {
this.client = client;
this.map = map;
}
@Override
public void run() {
try {
//获取输入流
//获取输出流
InputStream is=client.getInputStream();
OutputStream os=client.getOutputStream();
//套娃,读写方便
BufferedReader br=new BufferedReader(new InputStreamReader(is,"utf-8"));
PrintWriter pw=new PrintWriter(new OutputStreamWriter(os,"utf-8"),true);
//获取每个客户端的名字
String name=br.readLine();
//搞个欢迎语
System.out.println("欢迎来到吴亦凡选妃后宫群.....请踊跃发言提高选中率!");
//将每个客户端信息插入共享变量中,防止出现线程不安全问题
synchronized (this){
map.put(name,pw);
}
//无限读取当客户端信息
String line;
while((line=br.readLine())!=null){
/*
一个客户端发送信息,共享给所有人
*/
System.out.println(line);
sendMsg(line,pw,name);
}
} catch (IOException e) {
e.printStackTrace();
}
}
/*
服务器端群发消息
String line:数据
PrintWriter:输出流(代表当前自己)
String name:客户端名字
*/
private void sendMsg(String line,PrintWriter pw,String name){
for (PrintWriter pw1:map.values()){
if (!pw1.equals(pw)){//遍历集合中所有输出流,如果不是自己,则发送数据
pw1.println(line);
}
}
}
}
12.3.3
/*
服务端
1.创建服务器:ServerSocket对象,绑定对应的端口
2.通过accept()方法监听客户端请求,并将监听到的Socket存储起来
3.连接建立好,通过Socket来获取输入流(得到是客户端发送的请求信息)
*/
public class ServerDemo01 {
public static void main(String[] args) throws IOException {
Scanner scan=new Scanner(System.in);
//1.
ServerSocket server=new ServerSocket(8088);
//2.accept()监听连接到此server的socket,并返回
System.out.println("服务器启动,等待客户端连接>...");
Socket s= server.accept();
System.out.println("有一个客户端连接进来了....");
//3.获取输入流
InputStream is=s.getInputStream();
BufferedReader br=new BufferedReader(new InputStreamReader(is,"utf-8"));
//4.获取输出流
OutputStream os=s.getOutputStream();
PrintWriter pw=new PrintWriter(new OutputStreamWriter(os,"utf-8"),true);
//循环读取和输出
while(true){
//读(先读客户端发送的数据)
String line=br.readLine();
System.out.println(line);
//写(根据客户端发送的数据,响应)
String input=scan.nextLine();
pw.println("秀秀:"+input);
}
}
}