第一天 JAVA入门
第一节 DOS常用命令
cd -改变目录
- cd [盘符:][路径名][子目录名]
- cd.. 返回上一级目录
- cd \ 或 cd / 返回根目录
dir -列出当前目录下的文件以及子文件夹
- dir [盘符:][目录路径] [/p] [/s] [/w] [/o]
- /p 分页显示下一页内容,按任意键查看下一屏
- /s 显示所有目录及其子目录下所有文件
- /w 宽屏显示
- /o 分类顺序显示
md -创建目录
- md [盘符;][路径名]<文件夹名>
- md myFile 在当前目录下创建myFile文件夹
- md myPhoto\aaa\bbb 在当前目录下创建 myPhoto\aaa\bbb多级目录
- ==注意==md命令还可以创建以 . 开头的文件夹
copy -复制一个或一组文件到制定磁盘或目录中
- copy <源文件路径>[目标路径]
- copy a.txt d:\myFile 复制a.txt文件到d:\myFile文件夹中
- copy myFile myPhoto 只赋值myFile文件夹下的所有文件到myPhoto中,不包括子文件夹中的文件
- 如果要复制所有子文件下文件可使用:
- xcopy myFile myPhoto /s 不能复制空目录
- xcopy myFile myPhoto /e 复制空目录
rd -删除目录
- rd [盘符:][路径名]<子目录名>
- 只能删除空目录,不能删除当前目录
- rd myFile 删除myFile目录
- rd myFile\aaa 只能删除aaa目录,不能删除myFile目录
del -删除文件
- 删除目录中的一个或一组文件,不能删除目录
- del info.txt 删除info.txt文件
- del myFile* 删除myFile目录下所有的文件
其他命令
- cls 清空屏幕
- exit 退出dos命令行
- mspaint 打开画图板
- notepad 打开记事本
- echo 个人简历>简历.docx (echo 内容> . 创建非空文件 )
- type nul>a.mp3 (type nul>a.mp3 创建空文件)
第二节 JAVA平台
- Java SE 桌面、服务器、嵌入式环境、实时环境中使用的java应用程序
- Java EE 企业级应用
- Java ME 移动设备和嵌入式设备
第三节 JAVA跨平台原理
小结
1 计算机组成:硬件和软件 2 dos命令 cd 改变当前目录 dir 列出当前目录中文件和文件夹 md 创建目录 rd 删除目录 (空目录) del 删除文件 copy 复制 xcopy cls 清屏 exit 退出dos mspaint notepad type 创建空文件 type nul>aaa.txt echo 创建非空文件 echo 内容>bbb.txt
3进制 二进制 基本数字 0 1 二进制和十进制的转换 3.1 二进制转十进制 从右到左每一个数字乘以 2 的权次方 相加 3.2十进制转二进制 除2取余,逆序排列
4 java 历史 jdk8 5 java 运行核心机制 1 jvm java虚拟机 (跨平台的原理) 2 垃圾回收器 6 安装jdk、环境变量配置 JAVA_HOME : 修改path 7 开发java程序 第一步:编写源程序
第二步:编译 class文件 javac hello.java 第三步:运行 java Hello 8 输出方法: print() 输出没有换行 ,println(); 输出有换行 ,printf() 格式化输出 (了解) 9 注释 //单行注释 /* 多行注释 */
第二天 JAVA基本语法
第一节 标识符
- 标识符命名规则
- 由字母、数字、下划线、_、$组成
- 不能以字母开头
- 不能是java中的保留字和关键字
- 标识符命名习惯:
- 见名知意
- 变量名、方法名、参数名:驼峰命名法
- 类名:帕斯卡命名法
第二节 数据类型
- 基本数据类型:
- byte 1个字节,-128~127
- short 2个字节,-32768~32767
- int 4个字节
- long 8个字节
- float 4个字节,范围-3.40E+38~+3.40E+38,有效位7-8位
- double 8个字节,-1.79E+308~+1.79E+308,有效位15-16位
- char 2个字节,0-65535,65536个字符
- boolean 不确定
- 引用类型
- 类
- 接口
- 数组
- 枚举
- 转义字符
- \n \r \t \' \" \\
- 自动类型转换 byte ‐‐‐> short‐‐‐>int‐‐‐>long‐‐‐>float ‐‐‐>double char--->int
第三节 运算符和表达式
- 逻辑运算符
- ^异或
- !异或非
- ~按位取反
- 运算符的优先级
小结
1 标识符: 给包、类、方法名、变量、参数定义的一个字符序列。 2 标识符命名规范: 1>只能包括字母、数字、下划线、$ 2> 不能以数字开头 3>区分大小写 4>长度无限制(不要超过15) 5>不能是java关键字 3 标识符命名习惯: 1>见名知意 2>方法名、变量名、参数名首字母小写,后面首字母大写, 驼峰式命名法 3>类名采用首字母大写,帕斯卡命名法 4 关键字和保留字 public class static void 等等
import java.util.Scanner; //导入Scanner类 public class ScoreStat { public static void main(String[] args) { //创建对象 Scanner input = new Scanner(System.in); System.out.print("htmls的成绩是:"); int html = input.nextInt(); //html分数 int java = input.nextInt(); int sql = input.nextInt(); int diffen; //分数差 double avg; //平均分 //省略输出成绩单代码…… diffen = java ‐ sql; //计算Java课和SQL课的成绩差 System.out.println("Java和SQL的成绩差:" + diffen); avg = (html + java + sql) / 3; //计算平均分 System.out.println("3门课的平均分是: " + avg); } }
5 常量和变量 常量:固定的数据。 整型常量:10,20 浮点常量:3.14 字符常量: 'a' 布尔常量:true false 字符串常量: "xxxx","hello world" null常量 变量:本质就是内存中一块存储空间。
变量名,变量的名字
变量值,变量中存储的数据
变量类型
使用变量: 1》先声明,再赋值 int num; num=20; 2>声明的同时,赋值 int num2=20; 变量分类 1 按照类型 分: 基本数据类型变量和引用类型变量 2 按照声明的位置分: 局部变量和成员变量
6 数据类型 两大类:基本数据类型和引用数据类型 基本数据类型 : byte short int long float double char boolean 引用类型 : 类 String 、数组 、接口、 枚举
byte : 1个字节 -128-127 short :2 -32768-32767 int :4 long :8 float:4
double :8 char :2 0-65535 boolean :不确定 基本数据类型之间的转换 1 自动类型转换 : 表示范围小的转成表示范围大的 byte--->short---->int---->long --->float--->double char--->int byte short char 运算是转成int int常量和字符常量在合理的范围之内可赋值给byte short int char 2 强制类型转换 表示范围大的转成表示范围小的 7 运算符 算术运算符: + - * / % ++ -- 赋值运算符: = += -= *= /= %= 关系运算符: == != > < >= <= 逻辑运算符: & | ! ^ && || 位运算符: & | ~ ^ >> << >>> (了解) 字符串连接符 :+ 三目运算符 : X ?Y :Z 优先级 表达式 8 Scanner的使用
第三天 选择语句
switch穿透
- default无论位置在哪儿,只有不符合条件的时候才调用到,如果default在最下层,上层没有break语句,那么可以穿透到default语句,即default为一个特殊的case。
- 没有break则会一直穿透,直到遇到break或程序结束。
- 支持类型
- 基本数据类型:byte, short, char, int
- 包装数据类型:Byte, Short, Character, Integer
- 枚举类型:Enum
- 字符串类型:String(Jdk 7+ 开始支持)
小结
1 程序三种基本结构 顺序结构 选择结构 循环结构 java 选择结构: if 使用形式 if(条件){....} if(条件){...} else{...} if(条件){...} else if(条件){...}else if(条件){...} else{...} if(条件){ ... if(条件){...} } else{...}
switch (等值判断) switch(变量表达式){ case 常量1: .... break; case 常量2: .... break; } 变量类型:byte short int char String 枚举 case 常量不能重复 default 可以省略 break 不要省略,如果省略会出现case穿透
第四天 循环语句
标签
- break label跳出到label位置
- continue label跳出到label位置然后继续
- 反编译之后的代码是通过 !=实现label
小结
三种循环语句 while 先判断条件,再执行循环体 do while 先执行循环体,在判断条件 ,至少执行一次 for 先判断条件,再执行循环体 如果循环次数固定优先使用for ,如果次数不确定用while和do while 二重循环 外层循环执行一次,内存循环执行一遍。 跳转语句 break :跳出 swith和循环 continue: 跳出本次循环,继续下一次循环
第五天 方法
方法的重载
- 概念:同一个类中,方法名字相同,参数列表不同则是重载。
注意
- 参数列表的不同包括,参数个数不同,参数数据类型不同,参数顺序不同
方法的重载与方法的修饰符和返回值没有任何关系
- 命名法:帕斯卡(Pascal)命名法:,驼峰命名法
重写
java,重写时,返回值类型可以变吗?
jdk1.5之后 可以. 但是改变后的类型必须是修改前类型的子类型..比如下面的情况
class Test1 { public Object workO() { return new Object(); } }
class Test2 extends Test1 { @Override public String workO() { return new String(); } }
其中String是Object的子类型.
小结
1 方法:完成特定功能的一段代码。 声明方法 访问修饰符 其他修饰符 返回类型 方法名(参数列表){ 方法体 } public static void method1(){ } public static void method2(int x){ } public static int method(){ return 10; } 方法调用: 方法名(); 方法名(参数);
class PracticeDemo02 { public static void main(String[] args) { System.out.println(getNum(100)); System.out.println(getNum1(10)); }
//1.求1‐‐某个数之间可以被7整除的数的个数
public static int getNum(int n) {
int count = 0;
for(int i = 1;i <= n;i++) {
if(i % 7 == 0) {
count++;
}
}
return count;
}
}
2 方法重载 同一个类中,方法名相同,参数类别不同 (1) 参数列表不同: 个数不同,类型不同,顺序不同 (2)和修饰符、返回值没有关系 3 递归算法 : 在一个方法内部调用自己本身。 3.1求第30个斐波那契数 3.2 求1-100的和
第六天 数组排序查找
二分查找
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* @Auther: xbh
* @Date: 2019/8/7 09:00
* @Description:
*/
public class _$ {
public static void main(String[] args) {
int arr[] = new int[]{8, 7, 6, 5, 4, 3, 2, 1};
System.out.println(binarySearch(arr, 0, arr.length, 8));
}
public static int binarySearch(int a[], int fromIndex, int toIndex, int key){//arr ordey by desc(asc reverse)
int low = fromIndex;
int height = toIndex - 1;
while (low <= height){
int mid = (low + height) >>> 1;
if(a[mid] < key){
height = mid-1;
}else if(a[mid] > key){
low = mid + 1;
}else {
return mid;//key found
}
}
return -1;//key not found
}
}
排序
1. 冒泡排序
import java.util.Arrays;
import java.util.Scanner;
/**
* @author xbh
* 冒泡排序
*/
public class maopao {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.println("==========冒泡排序==========");
System.out.println("请输入要排序的数字个数");
int n = in.nextInt();//接收输入的数字个数
int[] nums = new int[n];//待排序数组
System.out.println("请输入" + n + "个数字");
for (int i = 0; i < n; i++) {
nums[i] = in.nextInt();
}
for (int i = 0; i < n-1; i++) {
for (int j = 0; j < n-1-i; j++) {
if(nums[j] > nums[j+1]){
int temp = nums[j];
nums[j] = nums[j+1];
nums[j+1] = temp;
}
}
}
System.out.println("排序后的数组为");
System.out.println(Arrays.toString(nums));
}
}
2. 选择排序
import java.util.Arrays;
import java.util.Scanner;
/**
* @author xbh
* 选择排序
*/
public class select {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n, index = -1;
int[] nums;
System.out.println("==========选择排序==========");
System.out.println("请输入要进行排序的数字个数");
n = in.nextInt();
nums = new int[n];
System.out.println("请输入n个数字");
for (int i = 0; i < n; i++) {
nums[i] = in.nextInt();
}
for (int i = 0; i < n-1; i++) {
int temp = nums[i];
for (int j = i+1; j < n; j++) {
if(nums[j] < temp){
temp = nums[j];
index = j;
}
}
if(nums[i] != temp){
temp = nums[i];
nums[i] = nums[index];
nums[index] = temp;
}
}
System.out.println("排序后的数组为");
System.out.println(Arrays.toString(nums));
}
}
3. 插入排序
import java.util.Arrays;
import java.util.Scanner;
/**
* @author xbh
* 插入排序
*/
public class insert {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.println("==========插入排序==========");
int n, i, j;
int[] nums;
System.out.println("请输入要排序数字的个数");
n = in.nextInt();
nums = new int[n];
System.out.println("请输入" + n + "个数字");
for (i = 0; i < n; ++i) {
nums[i] = in.nextInt();
}
for (i = 1; i < n; ++i) {
int temp = nums[i];
for (j = i; j > 0&&nums[j-1] > temp; --j) {
nums[j] = nums[j-1];
}
nums[j] = temp;
}
System.out.println("排序后的数组为");
System.out.println(Arrays.toString(nums));
}
}
4. 希尔排序(插入排序升级版)
import java.util.Arrays;
import java.util.Scanner;
public class XbhShellSort {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int[] nums;
int n;
System.out.println("==========希尔排序==========");
System.out.println("请输入要进行排序的数字个数:");
n = in.nextInt();
nums = new int[n];
System.out.println("请输入n个数字:");
for (int i = 0; i < n; i++) {
nums[i] = in.nextInt();
}
ShellSort(nums);
System.out.println("排序后的数组为:");
System.out.println(Arrays.toString(nums));
}
public static void ShellSort(int[] arr){
int N = arr.length;
//进行分组,最开始时的增量(gap)为数组长度的一把
for (int gap = N/2; gap > 0; gap /= 2){
//对各个分组进行插入排序
for (int i = gap; i < N; i++) {
//将arr[i]插入到所在分组的正确位置上
insert(arr, gap, i);
}
}
}
public static void insert(int[] arr, int gap, int i){
int inserted = arr[i];
int j;
//插入的时候按组进行插入(组内元素两两相隔gap)
for (j = i - gap; j >= 0 && inserted < arr[j]; j -= gap){
arr[j+gap] = arr[j];
}
arr[j+gap] = inserted;
}
}
[希尔排序] (https://blog.csdn.net/qq_39207948/article/details/80006224)
5. 快速排序
import java.util.Arrays;
import java.util.Scanner;
/**
* @author xbh
* 快速排序
*/
public class qsort {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int[] nums;
int n;
System.out.println("==========快速排序==========");
System.out.println("请输入要输入数字的个数");
n = in.nextInt();
nums = new int[n];
System.out.println("请输入" + n + "个数字");
for (int i = 0; i < n; i++) {
nums[i] = in.nextInt();
}
QSort(nums, 0, n-1);
System.out.println("排序后的数字如下");
System.out.println(Arrays.toString(nums));
}
public static void QSort(int[] a, int l, int r){
if(l >= r)//左右索引相同比较完成一组
return;
int i = l, j = r, t = a[l];
while (i < j){
while(i < j && a[j] >= t)
--j;
a[i] = a[j];
while (i < j && a[i] <= t)
++i;
a[j] = a[i];
}
a[i] = t;
QSort(a, l, i-1);
QSort(a, i+1, r);
}
}
6. 归并排序
import java.util.Arrays;
import java.util.Scanner;
/**
* @author xbh
* 归并排序
*/
public class mSort {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.println("==========归并排序==========");
System.out.println("请输入要排序的数字个数:");
int n = in.nextInt();//接收输入的数字个数
int[] nums = new int[n];//待排序数组
System.out.println("请输入" + n + "个数字");
for (int i = 0; i < n; i++) {
nums[i] = in.nextInt();
}
mergeSort(nums, 0, n-1);
System.out.println("归并排序后的数组为");
System.out.println(Arrays.toString(nums));
}
public static void merge(int[] a, int l, int mid, int r){
int[] b = a.clone();
int i, j, k = l;
for (i = l, j = mid+1;i <= mid && j <= r; k++) {
if(b[i] <= b[j]){
a[k] = b[i++];
} else if(b[i] > b[j]){
a[k] = b[j++];
}
}
while (i <= mid){
a[k++] = b[i++];
}
while (j <= r){
a[k++] = b[j++];
}
}
public static void mergeSort(int[] a, int l, int r){
if(l < r){
int mid = (l + r)>>1;
mergeSort(a, l, mid);
mergeSort(a, mid+1, r);
merge(a, l, mid, r);
}
}
}
[归并排序] (https://blog.csdn.net/k_koris/article/details/80508543)
7. 桶排序
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
public class XbhBasketSort {
public static void main(String[] args) {
// 输入元素均在 [0, 10) 这个区间内
float[] arr = new float[] { 0.12f, 2.2f, 8.8f, 7.6f, 7.2f, 6.3f, 9.0f, 1.6f, 5.6f, 2.4f };
bucketSort(arr);
printArray(arr);
}
public static void bucketSort(float[] arr) {
// 新建一个桶的集合
ArrayList<LinkedList<Float>> buckets = new ArrayList<LinkedList<Float>>();
for (int i = 0; i < 10; i++) {
// 新建一个桶,并将其添加到桶的集合中去。
// 由于桶内元素会频繁的插入,所以选择 LinkedList 作为桶的数据结构
buckets.add(new LinkedList<Float>());
}
// 将输入数据全部放入桶中并完成排序
for (float data : arr) {
int index = getBucketIndex(data);
insertSort(buckets.get(index), data);
}
// 将桶中元素全部取出来并放入 arr 中输出
int index = 0;
for (LinkedList<Float> bucket : buckets) {
for (Float data : bucket) {
arr[index++] = data;
}
}
}
/**
* 计算得到输入元素应该放到哪个桶内
*/
public static int getBucketIndex(float data) {
// 这里例子写的比较简单,仅使用浮点数的整数部分作为其桶的索引值
// 实际开发中需要根据场景具体设计
return (int) data;
}
/**
* 我们选择插入排序作为桶内元素排序的方法 每当有一个新元素到来时,我们都调用该方法将其插入到恰当的位置
*/
public static void insertSort(List<Float> bucket, float data) {
ListIterator<Float> it = bucket.listIterator();
boolean insertFlag = true;
while (it.hasNext()) {
if (data <= it.next()) {
it.previous(); // 把迭代器的位置偏移回上一个位置
it.add(data); // 把数据插入到迭代器的当前位置
insertFlag = false;
break;
}
}
if (insertFlag) {
bucket.add(data); // 否则把数据插入到链表末端
}
}
public static void printArray(float[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + ", ");
}
System.out.println();
}
}
8. 插入排序
import java.util.Scanner;
/**
* @author xbh
* 哈希排序
*/
public class XbhHashSort {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.println("==========插入排序==========");
int n, i, j;
int[] nums;
System.out.println("请输入要排序数字的个数");
n = in.nextInt();
nums = new int[100];
System.out.println("请输入" + n + "个数字");
for (i = 0; i < n; ++i) {
int k = in.nextInt();
nums[k]++;
}
System.out.println("排序后的数组为");
for (int k = 0; k < 100; k++) {
for (int l = 0; l < nums[k]; l++) {
System.out.print(k + " ");
}
}
}
}
9. 计数排序
import java.util.Arrays;
import java.util.Scanner;
/**
* 假如我们有一个数组a[10],范围都是0-10的整数
*
* index 0 1 2 3 4 5 6 7 8 9
* a 7 7 6 0 9 0 5 6 7 8
* 先创建出一个数组c,遍历数组a,统计一下每个元素有多少个
*
* index 0 1 2 3 4 5 6 7 8 9
* c 2 0 0 0 0 1 2 3 1 1
* 对c求前缀和,对于每个i,小于等于i的元素有c[i]个
*
* index 0 1 2 3 4 5 6 7 8 9
* c 2 2 2 2 2 3 5 8 9 10
* 创建一个临时数组b[10]用来输出最终结果,反向遍历数组a,
* 对于每个元素a[i],小于等于a[i]的元素有c[a[i]]个,那么a[i]的最终位置是c[a[i]]-1(如果下标从1开始,那么最终位置就是c[a[i]])
*
* i=8,a[i]=8,c[8]=9,小于等于8的元素有9个,所以b[8]=a[i]=8
*
* index 0 1 2 3 4 5 6 7 8 9
* b 8
* 原文:https://blog.csdn.net/qq_39942341/article/details/82379334
*/
public class XbhCountSort {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.println("=========计数排序==========");
int n, i, j;
int[] nums;
System.out.println("请输入要排序数字的个数:");
n = in.nextInt();
nums = new int[n];
System.out.println("请输入" + n + "个数字");
for (i = 0; i < n; ++i) {
nums[i] = in.nextInt();
}
int b[] = countSort(nums);
System.out.println("排序后的数组为");
System.out.println(Arrays.toString(b));
}
public static int[] countSort(int[]a){
int b[] = new int[a.length];
int max = a[0],min = a[0];
for(int i:a){
if(i>max){
max=i;
}
if(i<min){
min=i;
}
}//这里k的大小是要排序的数组中,元素大小的极值差+1
int k=max-min+1;
int c[]=new int[k];
for(int i=0; i<a.length; ++i){
c[a[i]-min] += 1;//优化过的地方,减小了数组c的大小
}
for(int i=1; i<c.length; ++i){
c[i] = c[i]+c[i-1];
}
for(int i=a.length-1; i>=0; --i){
b[--c[a[i]-min]] = a[i];//按存取的方式取出c的元素
}
return b;
}
}
10. 堆排序
import java.util.Arrays;
import java.util.Scanner;
public class XbhHeapSort {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int [] nums;
int n;
System.out.println("==========堆排序==========");
System.out.println("请输入要进行排序的数字个数");
n = in.nextInt();
nums = new int[n];
System.out.println("请输入n个数字:");
for (int i = 0; i < n; i++) {
nums[i] = in.nextInt();
}
int [] ans = heapSort(nums);
System.out.println("排序后的数组为:");
System.out.println(Arrays.toString(ans));
}
/**
* 选择排序-堆排序
* @param array 待排序数组
* @return 已排序数组
*/
public static int[] heapSort(int[] array) {
//这里元素的索引是从0开始的,所以最后一个非叶子结点array.length/2 - 1
for (int i = array.length / 2 - 1; i >= 0; i--) {
adjustHeap(array, i, array.length); //调整堆
}
// 上述逻辑,建堆结束
// 下面,开始排序逻辑
for (int j = array.length - 1; j > 0; j--) {
// 元素交换,作用是去掉大顶堆
// 把大顶堆的根元素,放到数组的最后;换句话说,就是每一次的堆调整之后,都会有一个元素到达自己的最终位置
swap(array, 0, j);
// 元素交换之后,毫无疑问,最后一个元素无需再考虑排序问题了。
// 接下来我们需要排序的,就是已经去掉了部分元素的堆了,这也是为什么此方法放在循环里的原因
// 而这里,实质上是自上而下,自左向右进行调整的
adjustHeap(array, 0, j);
}
return array;
}
/**
* 整个堆排序最关键的地方
* @param array 待组堆
* @param i 起始结点
* 音乐软件@param length 堆的长度
*/
public static void adjustHeap(int[] array, int i, int length) {
// 先把当前元素取出来,因为当前元素可能要一直移动
int temp = array[i];
for (int k = 2 * i + 1; k < length; k = 2 * k + 1) { //2*i+1为左子树i的左子树(因为i是从0开始的),2*k+1为k的左子树
// 让k先指向子节点中最大的节点
if (k + 1 < length && array[k] < array[k + 1]) { //如果有右子树,并且右子树大于左子树
k++;
}
//如果发现结点(左右子结点)大于根结点,则进行值的交换
if (array[k] > temp) {
swap(array, i, k);
// 如果子节点更换了,那么,以子节点为根的子树会受到影响,所以,循环对子节点所在的树继续进行判断
i = k; //这一步决定了下一个步骤执行到的是左子树还是右子树
} else { //不用交换,直接终止循环
break;
}
}
}
/**
* 交换元素
* @param arr
* @param a 元素的下标
* @param b 元素的下标
*/
public static void swap(int[] arr, int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
[堆排序] (https://blog.csdn.net/u013384984/article/details/79496052)
小结
熟练掌握eclipse的基本使用 熟练掌握数组的基本用法 熟练掌握数组的排序和查找方式
第七天 二维数组
第一节 Arrays工具类
public class _$ {
public static void main(String[] args) {
int[] a = new int[]{2, 33, -1, 223};
Arrays.binarySearch(a, 12);//二分查找
Arrays.sort(a);//排序
int[] b = Arrays.copyOf(a, a.length);//复制
Arrays.fill(a, 10);//填充
String s = Arrays.toString(a);//数组转字符串
}
}
此处可看源码
小结
1 Arrays工具类 的使用 binarySearch() //二分查找
/**
-
学校类
- @author wgy - @version 2.0 2018/03/20 */ public class School { /** 学校名称 */ String schoolName; //... /** - 招生方法 - @return total */ public void DrawStudent() { System.out.println(schoolName+"开始招生..."); } //... }
sort();// 排序 copyOf();// 复制数组 fill();// 填充 toString();//把数组转成字符串形式 2 方法的参数传递和返回值 1 方法的参数传递采用传值方式: 基本类型传递的实际数据 引用类型传递的地址 2 方法的返回值,返回数据,基本返回实际数据,引用类型返回地址。 3 二维数组,实际上是由一维数组组成,一维数组的每个元素还是一个数组。 4 二维数组的声明、初始化、访问(通过下标访问,遍历)
int[][] arr=new int[][]{{10,20},{30,40}}
5 调试技巧 1 添加断点 2 单步执行 F6单步跳过 F5单步进入 F8继续执行 ctrl+f12 终止调试 6 文档注释 帮助开发人员生成API文档
第八天 面向对象
第一节 面向对象的设计思想
- 类与对象之间的关系:类是对象的抽象(模板),对象是类的具体体现(实例)
- 类中方法的定义:在同一个类中,在静态方法中只能调用静态方法,在非静态方法中可以调用非静态方法和静态方法
小结
1 面向对象思想: 一种思维方式,着眼于找到一个具有特殊功能的具体个体,然后委托这个个体去做某件事情。 面向过程思想:一种思维方式,着眼于解决问题的过程和步骤。 2 类的概念 : 具有特殊功能的实体的集合[群体]
public class Test { public static void main(String[] args) { System.out.println("Hello World!"); } } public class Dog { String name; int age; int num; String hobby;
//提高代码的可读性,可维护性
//构造方法
public Dog() {
}
public Dog(String name) {
this.name = name;
}
public Dog(int age) {
this.age = age;
}
public Dog(String name,int age) {
this.name = name;
this.age = age;
}
public Dog(String name,int age,int num,String hobby) {
this(name,age);
this.num= num;
this.hobby = hobby;
}
}
3 对象:一个具体特殊功能的实体。
4 类和对象的关系:类是对象的抽象或模板,对象是类的具体或实例
5 定义类
public class Person{
//成员变量 属性
//方法
}
6 创建对象
类名 对象名=new 类名();
//使用对象的属性和方法
对象名.属性名
对象名.方法名();
对象的内存分配
栈
堆
方法区 ( 静态区,串池(常量池), 代码段)
7 构造方法:创建对象时调用的方法。
public 类名(){
}
默认构造方法
带参构造方法
构造方法重载
8 this关键字 :表示当前对象的引用
this.属性
this.方法
this();//调用其他的构造方法
第九天 封装继承
第一节 封装性
- 封装概念:在类中,对于不想被类外直接访问的成员变量,进行私有化,同时对外提供一个共有的方法为了访问私有成员。
第二节 static关键字
- static关键字只能修饰类成员,修饰成员变量和方法
- 静态属性和方法使用原则:
- 如果这个变量或方法,不属于每个对象,属于整个类,就用静态
- 如果这个对象只有一个,那么类中的属性和方法都用静态的,一般工具类中的方法静态的Arrays.方法名()
- 代码块:
- 局部代码块:声明在方法中的代码块
- 动态代码块:构造代码块或实例代码块
- 静态代码块:使用static修饰的动态代码块,在类加载时只自动执行一次
第三节 继承
- 继承的好处:
- 实现代码的重用和扩展
- 模拟现实世界的关系
第四节 访问权限
| | 本类 | 同包中类或同包子类 | 不同包子类 | 不同包类 | | ------------- | :--: | :----------------: | :--------: | :------: | | public | √ | √ | √ | √ | | protected | √ | √ | √ | × | | 默认[default] | √ | √ | × | × | | private | √ | × | × | × |
第五节 方法重写
- 方法名相同
- 方法参数,返回类型必须相同
- 访问修饰符不能比父类严格
小结
1 方法传参 返回值 方法传参采用传值 : 基本类型传递的数据本身,引用类型传递的是地址
* 1 方法名相同
* 2 方法参数 返回值类型必须相同
* 3 访问修饰符不能比父类严格
*
* java特殊 1.7 返回值 可以和父类兼容就可以,必须是引用类型
*/
public String printInfo() {
System.out.println("狗狗信息:昵称:"+super.nickname+" 颜色:"+super.color+" 品 种:"+super.strain+" 亲密度:"+this.love);
return "haha";
}
}
返回值:基本类型返回数据本身,引用类型返回地址。 2 封装 :隐藏类的实现细节,保证程序安全。 2个步骤 第一步:私有化成员变量 第二步:添加get和set方法 3 static关键字 静态: 成员变量或方法属于所有对象共有的,不属于每个对象,使用静态。 4 继承: 在原有类的基础上,产生一个新的类,在新的类中可以访问原有类中的非私有成员,并且可以添加一些自 己独有的成员,这个过程叫做继承,简单理解一个类继承另外一个类。 关键字 extends 继承符合 is a 关系 继承特点:1 单继承 2 继承具有传递性 5 super关键字 :表示父类的引用 super.属性 super.方法 super(); 6 包: 管理类的,解决类的命名冲突问题。 域名倒置+项目名 package com.qf.day09; 必须是第一句 导包 import java.util.*; 7 访问权限修饰符 public :公开的 protected:包含的 [default]:默认的 private:私有的 8 方法重写: 重写规则: 1 在继承中,方法名、参数列表、返回值必须和父类相同 2 访问权限不能比父类更严格
第十天 多态
第一节 多态
- 概念:在面向对象语言中,接口的多种不同的实现方式即为多态
- 实现步骤:
- 编写父类、子类,子类重写父类方法
- 运行时,使用父类变量,子类的对象
小结
1 Object 类 ,是所有的父类,默认继承Object equals()方法:判断两个对象是否相等 this==obj; == : 基本类型比较的数据,引用类型比较的地址。 equals() 默认 和== 一样, hashCode()方法:返回对象的地址 getClass()方法:返回类对象 toString()方法:返回对象的字符串形式, com.qf.day10.Person@123142; 重写 2 多态:同一个引用类型,使用不同的实例,执行不同的操作, 父类引用,子类对象 实现多态的要素或条件: 1 子类继承父类,子类必须重写父类的方法 2 使用父类变量,子类对象 多态表现形式: 1 使用父类作为方法的参数 2 使用父类作为方法的返回值 向上转型和向下转型 向上转型:子类转成父类 向下转型:父类转成子类 instanceof: 判断变量是否是某种类型。 if(pet instanceof Dog){ Dog dog=(Dog)pet; } 3 final 终止的 3.1 final 修饰变量 常量:只能赋值一次。 修饰成员变量 修饰局部变量 3.2 final 修饰方法 终止方法不能被重写,能被继承 3.3 final修饰类,终止类,不能被继承。
第十一天 抽象类和接口
第一节 抽象类
- final和abstract是否可以连用
- 两个关键字修饰方法时,final修饰的方法特点:可以被继承不能被重写;abstract修饰的方法特点:必须被重写;所以这两个关键字不能同时修饰同一个方法
- 两个关键字修饰类时:final修饰的类特点:不能被继承;abstract修饰的类特点:必须被继承;所以这两个关键字不能同时修饰同一个类
- 综上所述,final和abstract不可以连用
- final的类能否有abstract方法? 不能
- abstract类中能否有final方法? 可以
第二节 接口
-
概念:一种特殊的抽象类,这种抽象类中只包含常量和方法的定义,而没有方法的实现,从功能上讲:接口表示易中约定(规范)能力
-
优点:
- 扩展类的功能,保持对外接口一直
- 接口实现了多重继承,完成类和任何实现接口子类的通信和交互
- 降低代码之间的耦合性
-
特点:
- 接口不能创建对象,而且接口中没有构造方法
- 接口中的方法一般都是共有抽象方法:public abstract
- 接口中所有的属性都是共有静态常量属性:public static fianl
-
分类:
- 普通接口:在接口中可以声明抽象方法,和静态常量属性
- 常量群接口:在接口中只声明一组静态常量属性
- 标志性接口:在接口中没有抽象方法,也没有静态常量,作用为了标记某个类具有某个功能
-
特殊方法:
- jdk1.8之后接口中使用static关键字修饰的方法有方法体,静态方法需要有方法体
- jdk1.8之后接口中使用default关键字修饰的方法有方法体
-
抽象类和接口区别:
语法:
- 抽象类使用abstract,接口使用interface
- 抽象类中可以包含抽象方法,也可以包含非抽象方法,接口中只能包含抽象方法和静态常量,jdk1.8之后接口可以包含静态方法和默认方法
- 抽象类和接口都不能实例化
- 抽象类可以包含构造方法,接口中没有构造方法 功能:
- 抽象类一般表示同类事物,接口可以表示不同类事物
- 抽象类可以实现代码的重用,也可以约束子类的功能。接口就是约束实现类的功能,降低代码之间的耦合性。 使用场景:
- 程序或模块内部使用抽象类
- 程序架构或模块之间使用接口
小结
1 抽象类: 类实例化对象没有意思,所以使用抽象类, 抽象类不能实例化 2 abstract关键字 abstract 修饰类表示抽象类 abstract 修饰方法 抽象方法 4 抽象方法 抽象方法没有方法体 抽象方法被子类重写 包含抽象方法的类一定是抽象类
5 抽象类 抽象类不能实例化对象 抽象类可以包含抽象方法,也可以包含非抽象方法 抽象类的抽象方法必须被子类重写,除非子类也是抽象类 6 static final abstract final 和 abstract 不能一起使用 static和abstract不能一起使用 7接口 :语法:就是特殊的抽象类 功能:表示一个约定或能力 接口好处: 扩展类的功能,保持对外接口一致 接口实现了多重继承,完成类和任何实现接口子类的通信和交互 降低代码之间的耦合性
定义接口 public interface 接口名{ 常量 抽象方法 } jdk1.8 静态方法和默认方法 实现接口 class 类名 implements 接口名{ 重写接口中抽象方法 } 一个类可以实现多个接口,只能继承一个类 接口与接口的关系: extends ,多继承
public interface Inter1{
}
public interface Inter2{
}
public interface Inter3 extends Inter1,Inter2{
}
接口案例:
Usb案例 表示接口是一个约定 开火案例 表示接口是一个能力
第十二天 内部类和设计模式
第一节 内部类
1. 成员内部类
public class Out{
//成员变量
//成员方法
//成员内部类
访问权限 class In{
//成员变量
//成员方法
}
}
语法说明:
成员内部类的访问权限:任意的
如何创建成员内部类的对象:
由于成员内部类作为外部类的成员存在,若想访问类成员需要通过对象,所以成员内部类对象需要通过外部类对象创建
语法:
//创建外部类对象
Out o = new Out();
//通过外部类找到内部类,通过外部类对象创建内部类对象
Out.In i = o.new In();
一步完成:
Out.In i2=new Out().new In();
如何在成员内部类中访问外部类的成员:
-
当外部类的属性和内部类属性不同名时,可以直接访问
-
当外部类属性与内部类属性同名时,格式:外部类名.this.属性名;通过以上格式在内部类中访问外部类的同名属
-
成员内部类中不能包含静态成员
成员内部类的字节码文件格式:
外部类名$内部类名.class
代码实现:
public class Out {
//成员变量
int a = 2;
//成员方法
public void fun() {
System.out.println(a);
}
//成员内部类
public class In{
//成员内部类的成员变量
int a = 3;
//成员内部类的成员方法
public void fun() {
Out.this.fun();
System.out.println(a);
System.out.println(Out.this.a);
}
}
}
import memberInnerClass.Out.In;
public class Test {
public static void main(String[] args) {
//先创建外部类对象
Out o = new Out();
//System.out.println(o.a);
//o.fun();
//创建成员内部类的对象
//通过外类的对象创建成员内部类对象
In i = o.new In();
//System.out.println(i.b);
i.fun();
}
}
2. 局部内部类
作为局部成员存在,和局部变量平级
声明:
public class Outer{
//成员变量
//成员方法
//局部内部类所在的方法
public void fun(){
//功能代码
//局部内部类
访问权限 class Inner{
//局部内部类的成员变量
//局部内部类的成员方法
}
}
}
说明:
局部内部类的访问权限:只能是默认
如何创建局部内部类对象:
直接在局部内部类所在的方法中创建对象并调用方法
如何在局部内部类中访问外部类的属性:
- 不同名,直接访问
- 同名,外部类名.this.属性名
- 局部内部类中不能包含静态成员
- 局部内部类中访问局部变量,局部变量必须是final 常量 ,从jdk1.8之后final可以省略
字节码文件:
外部类名$编号内部类名.class
代码实现:
public class Outer {
//成员变量
int a = 3;
//成员方法
public void fun() {
class Inner{}
System.out.println("外部类的fun");
}
//局部内部类所在的方法
public void fun3() {
class In{}
//局部内部类
class Inner{
//成员变量
int a = 10;
//成员方法
public void fun() {
Outer.this.fun();
System.out.println(a);
System.out.println(Outer.this.a);
System.out.println("局部内部类的fun方法");
}
}
Inner i = new Inner();
i.fun();
}
}
public class Test {
public static void main(String[] args) {
Outer o = new Outer();
//局部内部类具有作用域,所以调用外部类对象的fun3方法时会声明局部内部类并创建对象调用方法
o.fun3();
}
}
3.静态内部类(重点)
声明:
public class Out{
访问权限 static class In{
}
}
说明:
访问权限:
任意的,一般使用public 使用static修饰的内部类,自动提升为普通类,相当于一个独立的类,和外部类级别相同
创建对象:
不需要外部类的对象,可以直接创建静态内部类的对象
格式:外部类.内部类 标识符 = new 外部类.内部类构造方法
访问外部类的成员:
- 静态内部类能直接访问外部类的静态成员
- 非静态的成员只能通过创建外部类对象访问
- 静态内部类中可以包含静态成员
字节码文件格式: 外部类名$内部类名.class(与成员内部类一样)
代码实现:
public class OuterClass {
static String s = "hello";
int a = 10;
public void fun() {
System.out.println("外部类的fun");
}
//静态内部类
public static class InnerClass{
int b = 20;
public void fun() {
System.out.println(s);
System.out.println();
System.out.println("内部类的fun");
}
}
}
import staticInnerClass.OuterClass.InnerClass;
public class Test {
public static void main(String[] args) {
//可以不创建外部类对象,直接创建静态内部类对象
InnerClass i = new InnerClass();
i.fun();
}
}
4.匿名内部类
什么是匿名内部类:
没有名字的内部类
原理产生原因:
由于接口和抽象类不能创建对象,若一个接口的实现类只需要使用一次,或一个抽象类的子类只需要使用一次, 可以使用匿名内部类,匿名内部类只能创建一个对象
代码实现:
public interface I {
public abstract void fun();
}
public class Test{
public static void main(Strng[]args){
I i = new I(){
public void fun(){
//功能代码
}
};
i.fun1();//使用匿名内部类的对象(接口引用)调用匿名内部类中重写的接口中的方法
}
}
匿名内部类的注意事项:
- 匿名内部类中必须把抽象方法全部实现
- 匿名内部类中可以声明独有的属性和方法,但是由于接口引用不能访问实现类中独有的属性和方法,所以一般不在匿名内部类中声明独有的属性和方法
- 匿名对象:若在匿名内部类中声明了独有的方法或属性,可以使用匿名对象访问,不能通过对象名.方法名()访问。
代码实现:
public static void main(String[]args){
//匿名对象调用成员方法
new I(){
public void fun(){}
public void fun1(){
System.out.println("匿名内部类的fun1");
}
}.fun1();//使用匿名内部类的匿名对象调用匿名内部类中独有的方法
//new Animal().eat();
}
匿名内部类的字节码文件格式:
测试类$编号.class
匿名内部类只能使用一次,即便两次声明的匿名内部类完全一致,也是两个匿名内部类
第二节 单例设计模式
-
实现方式: 单例分为懒汉式和饿汉式 核心: 构造方法私有:不能在类外随意创建对象 在类内部声明一个本类静态的对象作为属性 提供一个共有静态的方法用来获取本类对象
-
饿汉式代码实现:
public class Boss{ //属性 private String name; private int age; //私有化构造方法 private Boss(String name, int age){ this.name = name; this.age = age; } //私有静态本类对象作为属性 private static Boss boss = new Boss("马云",12); //提供共有静态方法获取本类对象 public static Boss getBoss(){ return boss; } }
-
懒汉式代码实现:
pulbic class King{ //属性 private String name; private int age; //私有构造方法 private King(String name, int age){ this.name = name; this.age = age; } //私有静态本类对象 private static King king; //共有静态方法获取本类对象 public static King getKing(){ if(king==null){ king = new King("唐太宗",12); } return king; } }
public class SingleTon { private SingleTon(){ //禁止反射破解 synchronized (SingleTon.class){ if(instance != null){ throw new RuntimeException("不能使用反射创建对象!"); } } } private static volatile SingleTon instance;//volatile,易挥发的,保证线程可见性,禁止指令重排序 public static SingleTon getInstance(){ if(instance == null){//检查,加速 synchronized (SingleTon.class){ if (instance == null){ instance = new SingleTon(); } } } return instance; } }
懒汉式和饿汉式有什么区别?
语法区别:
懒汉是调用方法时初始化对象,饿汉是声明同时初始化 使用上没有区别 存储空间:
在第一次获取单例类对象前,懒汉比饿汉节省空间 多线程操作时区别:
懒汉式存在线程安全问题,饿汉式没有线程安全问题
小结
1 内部类
1.1 成员内部类
成员内部类和非静态属性、方法 级别相同
创建成员内部类对象 1 先创建外部类对象,再创建内部类对象 Outer out=new Outer(); Outer.Inner inner=out.new Inner(); 2 一步到位 Outer.Inner inner=new Outer().new Inner();
注意: 成员内部类可以直接访问外部类的属性和方法 成员内部类中属性及方法和外部类的属性及方法相同时使用 外部类名.this.属性|方法 成员内部类中不能包含静态属性和方法
1.2 局部内部类 局部内部类,是包含在方法中,使用范围只限制在方法内。
Inner inner=new Inner(); inner.show(); 注意: 局部内部类可以直接访问外部类的属性和方法 如果局部内部类中属性及方法和外部类的属性及方法相同 外部类名.this.属性|方法 局部内部类不能包含静态属性和方法 局部内部类使用局部变量,局部变量必须是常量 final修饰
1.3 静态内部类 静态内部类:相当于一个外部类 只有内部类才可以是静态的
Outer.Inner inner=new Outer.Inner();
注意: 静态内部类可以直接访问外部类的静态属性和方法 静态内部类不能直接访问外部类的非静态属性和方法,需要创建对象访问 静态内部类中可以包含静态成员
1.4 匿名内部类 匿名内部类就是内部类的简写,主要用在接口和抽象类中创建对象
Usb mouse=new Usb(){ public void service(){
} }; mouse.service();
2 设计模式:特定问题的特定解决方法
2.1 单例设计模式 :
懒汉式
饿汉式
1 私有化构造方法 2 在类内部创建一个静态对象 3 通过一个公开的方法返回这个对象
2.2 简单工厂设计模式: 把对象的创建交给一个独立的工厂类创建。把对象的创建和使用分隔开 1 父类产品 2 子类产品 3 工厂 4 客户程序
第十三天 包装类和字符串
第一节 基本数据类型与其包装类
- Integer类型new时,如果范围在-128-127则存储在常量池内,如果超出该范围,则不断new新对象
第二节 字符串
- String new对象时,先向字符串常量池中寻找有无该字符串,没有则创建,然后在堆中创建该字符串常量的引用,若用intern()则指向字符常量池中的字符串
- StringBuilder类中的方法都是线程不安全的,而StringBuffer类中的方法都是线程安全的
- 字符串操作
- String s="xxx"; //串池 String s2=new String("xxx");//在堆中 //长度 s.length(); //查找位置 indexOf lastIndexOf //截取 subString(); //追加 concat(); //替换 replace(); //相等比较 equals() //比较 compareTo() //拆分 split() //去调空格 trim()
第三节 正则表达式
字符类
元字符 | 解释 |
---|---|
a | 字符a |
[abc] | 匹配a、b、c |
[^abc] | 任何字符,除了 a、b 或 c(否定) |
[a-zA-Z] | a 到 z 或 A 到 Z,两头的字母包括在内(范围) |
[a-d[m-p]] | a 到 d 或 m 到 p:[a-dm-p](并集) |
[a-z&&[def]] | d、e 或 f(交集) |
[a-z&&[ ^bc]] | a 到 z,除了 b 和 c:[ad-z](减去) |
[a-z&&[ ^m-p]] | a 到 z,而非 m 到 p:[a-lq-z](减去) |
预定义字符类
. 任意字符(与行结束符可能匹配也可能不匹配) \d 数字:[0-9] \w 单词字符:[a-zA-Z_0-9] 边界匹配器 ^ 行开头 $ 行结尾 数量: X? 一次或0次 X* 0次或多次(包括1次) X+ 一次或多次
X{n} 恰好n次 X{n, } 至少n次 X{n,m} 至少n次,不超过m次
扩展: Pattern类 :正则表达式编译类 Matcher类:匹配器
String string="我爱北京天安门,天安门上太阳升,天安门上有毛主席";
//获取
Pattern p2=Pattern.compile("天.门");
Matcher matcher2=p2.matcher(string);
while(matcher2.find()) {
String s=matcher2.group();
System.out.println(s);
}
//替换
String s3=string.replaceAll("天.门", "南天门");
System.out.println(s3);
小结
1 包装类: byte‐ Byte short‐Short int‐Integer long ‐Long float‐Float double‐Double char‐ Character boolean ‐Boolean
Integer
1 如果使用 Integer 创建对象,数据在堆中存放
2 Integer i=100; Integer i2=100;//是同一个对象 ,Integer中有缓冲区
3 装箱 基本类型转成包装类 拆箱 包装类转成基本类型
2 基本类型和字符串之间
2.1基本类型转成字符串
int i=5;
String s=i+"";
String.valueof(i);
2.2字符串转成基本类型
String s="12";
int i=Integer.parseInt(s);
3 字符串操作
String s="xxx"; //串池
String s2=new String("xxx");//在堆中
//长度
s.length();
//查找位置
indexOf
lastIndexOf
//截取
subString();
//追加
concat();
//替换
replace();
//相等比较
equals()
//比较
compareTo()
//拆分
split()
//去调空格
trim()
4 StringBuffer 增强的String, 追加 插入 替换
StringBuilder jdk1.5
区别:
1 功能一样
2 StringBuffer线程安全,StringBuilder线程不安全
3 StringBuilder效率高
5 正则表达式
[a‐z]
[a‐zA‐Z]
\d 0‐9 \w a‐zA‐A_0‐9
.任意字符
^ 表示开始
$ 表示结束
-
0次或多次
-
1次或多次 ? 0或1次 {n} n次 {n,} 大于等于n次 {n,m} 大于等于次,小于等于m次
1 匹配
2 拆分
3 获取
4 替换
Pattern 正则表达式类 Matcher 匹配器类
第十四天 常用类和异常
第一节 常用基础类
- Date类
-
判断两个日期对象的前后
Date date1 = new Date();//获取当前系统时间 Date date2 = new Date(10000);//获取从标准基准时间起10000毫秒的时间点 boolean boo1 = date1.after(date2);//若date1在date2之后,则返回true,否则返回false boolean boo2 = date1.before(date2);//若date1在date2之前,则返回true,否则返回false
-
比较两个日期对象
Date date1 = new Date(); Date date2 = new Date(10000); int i = date1.compareTo(date2);
-
获取时间
Date date = new Date(); long l = date.getTime();
-
修改时间
Date date = new Date(); date.setTime(1000);
-
Calendar类
-
获取一个Calendar对象:
Calendar c = Calendar.getInstance();//getInstance方法是一个静态的方法,直接通过类名调用 System.out.println(c);
-
获取某个日历对象的指定属性的值:
/* get(int field) 参数就是指定的属性,可以使用静态常量属性名代替int值 */ //注意:获取日期属性,不能直接用c.DATE,DATE属性时静态常量,所有Calendar类对象都共享并相同的值 Calendar c = Calendar.getInstance(); System.out.println(c.get(Calendar.DATE));//获取c对象所表示日历的日期属性值
-
修改某个日历对象的指定属性值:
/* set(int field, int value) 第一个参数表示属性,第二个参数表示修改的值 */ Calendar c = Calendar.getInstance(); c.set(Calendar.YEAR, 2017);
-
获取某个属性所表示的最大、最小值
/* getMaximum(int field) 获取属性的最大值 getMinimum(int field) 获取属性的最小值 */ Calendar c = Calendar.getInstance(); int max = c.getMaximum(Calendar.DATE);//获取日期的最大值,无论c表示几月份,max的值都是31 int min = c.getMinimum(Calendar.DATE);//获取日期的最小值,无论c表示几月份,min的值都是1
-
获取指定Calendar对象的指定属性的最大、最小值
/* getActualMaximum(int field) 获取指定日历的指定属性的最大值 getActualMinimum(int field) 获取指定日历的指定属性的最小值 */ Calendar c = Calendar.getInstance(); int max = c.getActualMaximum(Calendar.DATE); //若c表示的1月,则max值为31;若c表示的4月,则max值为30; int min = c.getActualMimimum(Calendar.DATE);//无论c表示几月份,min值都是1
-
-
SimpleDateFormat类
-
格式化日期
/* format(Date date) 将date对象格式化成指定格式的日期字符串 */ String format = "yyyy‐MM‐dd a hh:mm:ss"; SimpleDateFormat sdf = new SimpleDateFormat(format); Date date = new Date(); String dateStr = sdf.format(date);
-
解析日期
/* parse(String str) 将str对象解析成一个日期对象 */ String dateStr = "2017‐12‐01 上午 10:10:10"; String format = "yyyy‐MM‐dd a hh:mm:ss"; SimpleDateFormat sdf = new SimpleDateFormat(format); Date date = sdf.parse(dateStr);
-
-
Math类
-
平方根、立方根
int a = 8; System.out.println(Math.sqrt(a));//结果为8的正平方跟 System.out.println(Math.cbrt(a));//结果为8的立方根
-
四舍五入
double a = 3.6; System.out.println(Math.round(a));//结果为4
-
随机数
//产生一个3~9之间的随机数 int a = (int)(Math.random()*(9‐3+1)+3);
-
-
Random类
若long种子确定,则在不同程序中,相同次数产生的随机数是相同的
-
产生随机数
Random random = new Random(10);//以10为种子,使用线性同余公式产生伪随机数 int i1 = random.nextInt();//产生一个随机整数 int i2 = random.nextInt(10);//产生一个10以内的随机整数 double d = random.nextDouble();//产生一个随机double值 boolean b = random.nextBoolean();//产生一个随机boolean值
-
修改种子
Random random = new Random(10); random.setSeed(20);//将随机数种子设置为20
-
-
System类
-
获取系统时间
long time1 = System.currentTimeMillis();//获取当前时间,毫秒数 long time2 = System.nanoTime();//获取当前时间,毫微秒
-
强制退出虚拟机
System.exit();//强制退出当前正在执行的虚拟机0 非0
-
垃圾回收处理机制
package day14; public class Person { private String name; private int age; public Person(String name, int age) { super(); 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; } @Override protected void finalize() throws Throwable { System.out.println("垃圾回收器回收了...."+name); } } public static void main(String[] args) { //垃圾回收,相当于匿名对象 Person person=new Person("张三1", 20); new Person("张三2", 20); new Person("张三3", 20); //回收下垃圾 System.gc(); new Person("张三4", 20); new Person("张三5", 20); new Person("张三6", 20); }
-
-
Runtime类
-
exec(command) 在单独的进程中执行指定的字符串命令
//1创建Runtime对象 Runtime runtime=Runtime.getRuntime(); //2exec 启动一个进程 Process process=runtime.exec("qq"); //3关闭进程 //程序休眠 Thread.sleep(5000); process.destroy();
-
exit(int status) 退出jvm System.exit(); 内部调用runtime.exit();
Runtime runtime=Runtime.getRuntime(); runtime.exit(0);
-
获取jvm的内存
public static void runtime2() { Runtime runtime=Runtime.getRuntime(); long totalMemory=runtime.totalMemory(); long freememory=runtime.freeMemory(); long maxmemory=runtime.maxMemory(); System.out.println("totalMemory:"+(totalMemory/1024/1024)); System.out.println("freememory:"+(freememory/1024/1024)); System.out.println("maxmemory:"+(maxmemory/1024/1024)); }
-
修改jvm内存
- 修改堆初始内存大小 ‐Xms300m
- 修改堆最大内存大小 ‐Xmx4000m
- 修改栈空间大小
‐Xss2m jdk1.5之前256k jdk1.5之后1m
-
枚举
异常
-
概念:
程序中异常:在程序执行过程中由于设计或设备原因导致的程序中断的异常现象叫做异常。 异常处理:Java编程语言使用异常处理机制为程序提供了异常处理的能力。
-
异常的分类
常见的运行时异常
非运行时异常(编译时异常)
非运行时异常:编译异常或检查异常,在程序设计过程中,编译时就会被发现,但是执行时可能发生也可能不发生的异 常,为了程序不报错可以执行,那么这一类异常必须进行相应的处理。 Exception的子类包括Exception,除了RuntimeExcption之外都属于编译时异常。
-
自定义异常类
package com.qf.day14_4; /** * 性别异常 * @author wgy * */ public class SexException extends Exception { public SexException() { super(); // TODO Auto‐generated constructor stub } public SexException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); // TODO Auto‐generated constructor stub } public SexException(String message, Throwable cause) { super(message, cause); // TODO Auto‐generated constructor stub } public SexException(String message) { super(message); // TODO Auto‐generated constructor stub } public SexException(Throwable cause) { super(cause); // TODO Auto‐generated constructor stub } } 使用 public void setSex(String sex) throws Exception { if(sex.equals("男")||sex.equals("女")) { this.sex = sex; }else { //抛出异常 throw new SexException("性别有错误"); } }
小结
常用类:
1 Date 表示一个瞬间时间
Date date=new Date();
2 Calendar类日历
Calendar calendar=Calender.getInstance();
calendar.get(Calendar.YEAR);
calendar.get(Calender.MONTH);
calender.set(); 3 SimpleDateFormat:实现时间和字符串之间的转换。 3.1把日期格式的字符串转成 时间对象 String s="2011‐10‐1"; SimpleDateFormat df=new SimpleDateFormat("yyyy‐MM‐dd"); Date date=df.parse(s); 3.2把时间转成某种格式的字符串 // 2015/10/1 SimpleDateFormat df=new SimpleDateFormat("yyyy/MM/dd"); String s=df.format(date); 4 Math数学类 5 Random 随机数类 6 System 类 arrayCopy 数组复制 exit 退出jvm gc 回收垃圾 currentTimeMills(); 7 Runtime 运行时 Runtime runtime=Runtime.getRuntime();
exec(); 执行命令
freeMemory(); //空闲内存
totalMemory();//总内存
maxMemory();//最大内存
exit();//退出jvm
8 枚举 本质就是一个终止类,里面都是静态常量
9 异常:程序中出现不正常的情况。 异常处理: try catch finally throw throws
try{....}catch(){...} try{...}catch(){...}finally 多重catch try{...}finally{...}
throws 声明抛出异常 throw 抛出异常
自定义异常: 继承Exception或RuntimeException,调用构造方法
第十五天 集合
第一节 概念
第二节 Collection接口
方法名 | 描述 |
---|---|
add(E e) | 确保此 collection 包含指定的元素(可选操作)。 |
addAll(Collection<? extends <br/>E> c) | 将指定 collection 中的所有元素都添加到此 collection 中(可选操作)。 |
clear() | 移除此 collection 中的所有元素(可选操作)。 |
contains(Object o) | 如果此 collection 包含指定的元素,则返回true。 |
containsAll(Collection<?> c) | 如果此 collection 包含指定 collection 中的所有元素,则返回 true。 |
equals(Object o) | 比较此 collection 与指定对象是否相等。 |
isEmpty() | 如果此 collection 不包含元素,则返回true。 |
iterator() | 返回在此 collection 的元素上进行迭代的迭代器。 |
remove(Object o) | 从此 collection 中移除指定元素的单个实例,如果存在的话(可选操作)。 |
removeAll(Collection<?> c) | 移除此 collection 中那些也包含在指定 collection 中的所有元素(可选操作)。 |
retainAll(Collection<?> c) | 仅保留此 collection 中那些也包含在指定 collection 的元素(可选操作)。 |
size() | 返回此 collection 中的元素数。 |
toArray() | 返回包含此 collection 中所有元素的数组。 |
第三节 泛型
-
泛型使用时的注意事项
1)泛型不能在类中声明静态属性、常量 final修饰的属性必须在声明的同时初始化,所以泛型不能声明常量 static修饰的属性是静态属性,先于对象,泛型类型取决于创建对象时传入的实际类型,所以泛型不能声明静态属性 综上所述:不能使用泛型声明静态属性、常量
2)泛型不能在类中初始化数组,但是可以声明数组 初始化数组时需要给元素进行分配空间,但是泛型类型不确定无法分配空间
3)在类中不能使用泛型声明参数个数相同的重载方法 当一个类中有两个泛型时,创建对象时,两个泛型使用相同类型替换,那么重载方法就是相同的方法(同名,参数列表 也相同)
4)使用不同实际类型创建出的泛型类对象的引用不可以相互赋值
-
受限泛型(理解即可)
1)<?>:表示任意类型 2)<? extends T>:表示T类或者T类的子类 3)<? super T>:表示T类或者T类的父类
第四节 迭代器
第五节 List接口
方法名 | 描述 |
---|---|
add(int index, E element) | 在列表的指定位置插入指定元素(可选操作)。 |
addAll(int index, Collection<? extends E> c) | 将指定 collection 中的所有元素都插入到列表中的指定位置(可选操作)。 |
containsAll(Collection<?> c) | 如果列表包含指定 collection 的所有元素,则返回true。 |
get(int index) | 返回列表中指定位置的元素。 |
indexOf(Object o) | 返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。 |
lastIndexOf(Object o) | 返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1。 |
listIterator() | 返回此列表元素的列表迭代器(按适当顺序)。 |
remove(int index) | 移除列表中指定位置的元素(可选操作)。 |
set(int index, E element) | 用指定元素替换列表中指定位置的元素(可选操作)。 |
subList(int fromIndex, int toIndex) | 返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图。 |
- List接口
List接口的特点:有序的,可以重复 - ArrayList实现类 存储结构:数组 适合遍历查找
- LinkedList实现 存储结构:双向链表 适合做添加,删除
- Vector向量集合 Stack栈 (先进后出)
小结
1 集合概念,用来存储一组数据的容器。和数组类似,数组是长度固定的,集合长度可以变化。数组能存储基本类型和引 用类型,集合只能存储引用类型。 2 Collection接口,父接口, add() remove() clear() contains() iterator() 3 Collection有两个子接口 List和Set 4 泛型:本质使用数据类型作为参数传递 4.1 定义泛型类 泛型方法 泛型接口 4.2 使用 创建类对象,指定泛型的实际类型 4.3 泛型限制,<?> 表示任何类型
<? extends T> 表示泛型上限, T类型或T的子类 <? super T> 表示泛型下限,T类型或T的父类 5 Iterator迭代器 hasNext(); next(); 6 List接口 List接口的特点:有序的,可以重复 7 ArrayList实现类 存储结构:数组 适合遍历查找 8 LinkedList实现 存储结构:双向链表 适合做添加,删除 9 Vector向量集合 Stack栈 (先进后出) ------ # 第十六天 Set和Map ## 第一节 Set接口 - 存储特点:相对无序存储,不可以存储相同的元素(排重),不能通过下标访问 - Set常用实现类 1. HashSet 此类实现Set接口,由哈希表(实际上是一个HashMap实例)支持。它不保证set的迭代顺序;特别是它不保证该顺序恒久 不变。此类允许使用null元素。 Hash:哈希——实际含义散列,就是一种算法,把任意长度的输入通过散列算法变换成固定长度的输出,该输出就是散列 值。 哈希表:数组加链表,既有数组的优点也有链表的优点。 存储特点: 相对无序存储,不可以存储相同元素(排重),通过哈希表实现的集合 2. LinkedHashSet LinkedHashSet类是具有可预知迭代顺序(相对有序)的Set接口的哈希表和链接列表实现。是HashSet的子类。 存储特点: 有序存储,不可以存储相同元素(排重),通过链表实现的集合的有序。 LinkedHashSet集合的元素排重与HashSet集合排重方法一致。 3. TreeSet集合 TreeSet集合是可以给元素进行重新排序的一个Set接口的实现。使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的Comparator进行排序,具体取决于使用的构造方法。 存储特点: 无序存储,排重,通过二叉树实现的集合,可以给元素进行重新排序 4. SortedSet接口 TreeSet除了实现了Set接口外,还实现了SortedSet接口 | 方法名 | 描述 | | ---------------------------------- | ------------------------------------------------------------ | | first() | 返回此 set 中当前第一个(最低)元素。 | | last() | 返回此 set 中当前最后一个(最高)元素。 | | headSet(E toElement) | 返回此 set 的部分视图,其元素严格小于toElement。 | | tailSet(E fromElement) | 返回此 set 的部分视图,其元素大于等于fromElement。 | | subSet(E fromElement, E toElement) | 返回此 set 的部分视图,其元素从 fromElement(包括)到toElement(不包括)。 | 5. TreeSet集合的元素排序 ```java 定制排序 public class Person implements Comparable<Person> { private String name; private int age; public Person() { super(); } public Person(String name, int age) { super(); this.name = name; this.age = age; } @Override public String toString() { return "Person [name=" + name + ", 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; } @Override //重写compareTo方法,按照年龄升序,若年龄相同按照姓名降序排序 public int compareTo(Person o) { //Person中有两个属性,此方法中必须对name和age两个属性都进行比较,否则将无法正确排重 if(this.age != o.age) { return this.age ‐ o.age; }else { return o.name.compareTo(this.name); } } } ``` ```java public class Animal { private String name; private int age; @Override public String toString() { return "Animal [name=" + name + ", 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 Animal(String name, int age) { super(); this.name = name; this.age = age; } public Animal() { super(); } } public class AnimalDemo{ public static void main(String[]args){ //创建一个TreeSet集合,使用Comparator接口的匿名内部类的匿名对象作为比较器 TreeSet<Animal> treeSet = new TreeSet<>(new Comparator() { @Override public int compare(Animal o1, Animal o2) { if(o1.age!=o2.age) { return o2.age ‐ o1.age; }else { return o1.name.compareTo(o2.name); } } }); //添加元素 treeSet.add(new Animal("大黄", 1)); treeSet.add(new Animal("旺财", 2)); //遍历集合 Iterator<Animal> it = treeSet.iterator(); while(it.hasNext()){ System.out.println(it.next()); } } } ``` ## 第二节 Map接口 - 概述 Map接口是将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。 - Map接口的常用方法 | 方法名 | 描述 | | --------------------------------------- | ------------------------------------------------------------ | | entrySet() | 返回此映射中包含的映射关系的 Set 视图 | | keySet() | 返回此映射中包含的键的 Set 视图。 | | putAll(Map<? extends K,? extends <V> m) | 从指定映射中将所有映射关系复制到此映射中(可选操作) | | remove(Object key) | 如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。 | | size() | 返回此映射中的键-值映射关系数。 | - Map常用实现类 1. HashMap 基于哈希表的Map接口的实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序。 存储特点: 相对无序存储,元素以键值对形式存在,键不可以重复,值可以重复,元素整体排重,可以快速的通过键查找到 所对应的值,通过哈希表实现的集合。 Map集合的排重,只需要重写键所属的类的hashCode和equals方法即可。 ```java //Person作为键 public class Person { private String name; private int age; public Person() { super(); } public Person(String name, int age) { super(); 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; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } @Override public int hashCode() { return age + name.length(); } @Override public boolean equals(Object obj) { if(obj instanceof Person) { Person p = (Person) obj; if(p.name.equals(name)&&p.age==age) { return true; } } return false; } } //Dog类作为值 public class Dog { private String name; public Dog(String name) { super(); this.name = name; } public Dog() { super(); } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Dog [name=" + name + "]"; } } //测试类 public class Demo { public static void main(String[] args) { HashMap<Person, Dog> map = new HashMap<>(); map.put(new Person("zhangsan", 12), new Dog("大黄")); map.put(new Person("lisi", 12), new Dog("旺财")); map.put(new Person("zhangsan", 12), new Dog("二哈")); } } ``` 2. LinkedHashMap LinkedHashMap集合是具有可预知迭代顺序的Set接口的哈希表和链接列表实现。此实现与HashSet的不同之外在于,后者维护着一个运行于所有条目的双重链接列表。用法与HashSet类似。 存储特点: 有序存储,元素排重,通过链表实现的集合。 3. Hashtable 此类实现一个哈希表,该哈希表将键映射到相应的值。任何非null对象都可以用作键或值。 存储特点: 相对无序存储,元素排重,通过哈希表实现的集合 4. HashMap与Hashtable的区别 1)Hashtable线程安全的,而HashMap线程不安全的 2)Hashtable中不允许存在null的键和null值,但是HashMap中允许null的键和null值 - Map集合的遍历 1. 使用keySet方法与get方法结合 ```java public class Demo { public static void main(String[] args){ HashMap<String, Integer> map = new HashMap<>(); map.put("aaa", 12); map.put("bbb", 13); map.put("ccc", 14); //通过keySet获取map中所有键 Set<String> set = map.keySet(); //获取set的迭代器 Iterator<String> it = set.iterator(); while(it.hasNext()){ String s = it.next(); //通过迭代的键,找到对应的值,一起输出 System.out.println(s+"‐‐‐"+map.get(s)); } } } ``` 2. 使用Map.Entry方法: ```java public class Demo { public static void main(String[] args) { HashMap<String, Integer> map = new HashMap<>(); map.put("aaa", 111); map.put("bbb", 222); map.put("ccc", 333); //将map转成一个Set集合 Set<Map.Entry<String, Integer>> set = map.entrySet(); //遍历set Iterator<Map.Entry<String, Integer>> it = set.iterator(); while(it.hasNext()) { System.out.println(it.next()); } } } ``` - TreeMap 存储结构:红黑树 ## 第三节 Collections工具类 - 同步线程 ```java /* static <T> Collection<T> synchronizedCollection(Collection<T> c) 返回指定 collection 支持的同步(线程安全的)collection。 static <T> List<T> synchronizedList(List<T> list) 返回指定列表支持的同步(线程安全的)列表。 static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) 返回由指定映射支持的同步(线程安全的)映射。 static <T> Set<T> synchronizedSet(Set<T> s) 返回指定 set 支持的同步(线程安全的)set。 */ Collection c = Collections.synchronizedCollection(new ArrayList()); List s = Collections.synchronizedList(new ArrayList()); Map m = Collections.synchronizedMap(new HashMap()); ``` - 排序 ```java /* static <T extends Comparable<? super T>> void sort(List<T> list) 根据元素的自然顺序 对指定列表按升序进行排序。 */ ArrayList<Integer> list = new ArrayList<>(); list.add(‐10); list.add(5); list.add(3); list.add(7); System.out.println(list); Collections.sort(list);//默认升序 System.out.println(list); ``` - 将集合中的元素进行反转 ```java /* static void reverse(List<?> list)
反转指定列表中元素的顺序。 */ Collections.reverse();
- 将集合元素打乱
```java
/*
static void shuffle(List<?> list)
使用默认随机源对指定列表进行置换。
*/
-
获取集合中的最大值、最小值
/* static T max(Collection coll) 根据元素的自然顺序,返回给定 collection 的最大元素。 static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll) 根据元素的自然顺序 返回给定 collection 的最小元素。 */ int n1 = Collections.max(list);
-
替换
/* static <T> boolean replaceAll(List<T> list, T oldVal, T newVal) 使用另一个值替换列表中出现的所有某一指定值 */ //原因:List集合是不排重的,使用新的元素将集合中出现的所有的旧的元素替换掉 Collections.replaceAll(list,5,100);
-
统计指定元素在集合中出现的次数
/* static int frequency(Collection<?> c, Object o) 返回指定 collection 中等于指定对象的元素数。 */ int num = Collections.frequency(list,5);
-
二分法查找
/* static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key) 使用二分搜索法搜索指定列表,以获得指定对象。 */ //前提:必须是排好序的集合 int index = Collections.binarySearch(list,‐10); //注意:Collections工具类中的方法只操作Collection接口,主要操作的是List接口
-
集合和数组的转换
package com.qf.day16; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class Demo2 { public static void main(String[] args) { //arrayToList(); //listToArray(); arrayToList2(); } //1数组转成集合,集合不能做添加、删除操作 public static void arrayToList() { String[] names= {"张三","李四","王五"}; List<String> list=Arrays.asList(names); System.out.println("list:"+list); //添加 //list.add("赵六"); //删除 //list.remove(0); //System.out.println("添加或删除之后list:"+list); list.set(0, "本伟"); System.out.println("替换之后:"+list); } //2 集合转成数组 public static void listToArray() { ArrayList<String> arrayList=new ArrayList<>(); arrayList.add("苹果"); arrayList.add("小米"); arrayList.add("华为"); arrayList.add("三星"); //数组 String[] arr=arrayList.toArray(new String[0]); System.out.println("‐‐‐‐‐‐‐‐遍历数组‐‐‐‐‐‐‐"); for (String string : arr) { System.out.println(string); } } //3特殊 public static void arrayToList2() { Integer[] nums= {10,20,5,8,9}; List<Integer> arraylist=Arrays.asList(nums); for (Integer integer : arraylist) { System.out.println(integer); } } }
小结
Collection
|_____List (特点:有序的,可以重复)
|___ArrayList (存储结构:数组)
|___LinkedList (链表)
|___Vector 数组
|___Stack 数组(栈)先进后出
|
|_____Set(特点:无序的,不能重复)
|__HashSet 哈希表(数组加链表) 1 执行hashCode()来计算存储的位置 ,2 执行equals比较
结果
|LinkedHashSet 哈希表 可以保证顺序
|TreeSet 自平衡红黑二叉树 1 元素要实现Comparable接口 ,2 定制比较器
Comparator
Map(特点:存储键值对,键不能重复,值可以重复,无序)
| HashMap 哈希表 1 执行hashCode()来计算存储的位置 ,2 执行equals比较结果
| Hashtable 哈希表 不能存储null键和null值,线程安全的 jdk1.0
|_______LinkedHashMap 哈希表 可以保证顺序
|_______TreeMap 自平衡红黑二叉树 1 key 要实现Comparable接口 ,2 定制比较器 Comparator
第十七天 文件和流
第一节 File类
-
File类作用 在java程序中,对磁盘文件进行描述的类。文件和目录路径名的抽象表示形式。
-
//网络\\本地 Linux相反
-
代码实现一:
File file = new File("d:\\a.txt");//创建一个文件对象,表示d盘的a.txt文件 System.out.println(file.canExecute()); System.out.println(file.canWrite()); System.out.println(file.canRead()); System.out.println(file.isHidden());
-
代码实现二:
File file = new File("d:\\a"); File f = new File("d:\\b.txt"); System.out.println(file.compareTo(f)); //exists方法:判断文件抽象路径表示的文件或目录是否存在 System.out.println(file.exists()); //createNewFile方法:创建一个新的空文件(若存在则创建失败) System.out.println(f.createNewFile()); //delete方法:只能删除文件和空文件夹,非空文件夹不能使用delete方法删除 System.out.println(f.delete()); System.out.println(file.delete());
-
代码实现三
public class Demo { public static void main(String[] args) throws IOException { //现有d:\\a\\b\\hello.txt //要求:在hello.txt相同的目录下创建出一个world.txt文件 File f1 = new File("d:\\a\\b\\hello.txt"); System.out.println(f1.getPath()); fun(f1); } //设计一个方法,在某个文件的相同目录下创建出一个新文件(新建文本文档.txt) /* * 分析: * 返回值:不需要 * 参数:一个File对象 * getParent方法 * createNewFile方法 * 构造方法 */ public static void fun(File file) throws IOException { //获取父路径 String parent = file.getParent(); //创建File对象 File f = new File(parent, "新建文本文档.txt"); //创建新文件 f.createNewFile(); } }
-
代码实现四
File file = new File("d:\\a\\hello.txt"); System.out.println(file.exists()); System.out.println(file.isDirectory());//判断一个File对象是否是文件夹 System.out.println(file.isFile());//判断一个File对象是否是文件 String[] files = file.list();//获取文件夹中所有子文件夹和文件的名称(字符串形式) System.out.println(files.length); for(String s:files) { System.out.println(s); } File[] fs = file.listFiles();//获取文件件中所有子文件夹和文件的抽象路径(File对象) System.out.println(fs.length); for(File f:fs) { System.out.println(f); } File file = new File("d:\\aa\\bb\\cc\\dd\\a.txt"); //在创建一个文件时,需要先判断父目录是否存在,若不存在则创建父目录 File parent = file.getParentFile(); if(!parent.exists()) { System.out.println(parent.mkdirs());//创建一个新的空文件夹 } System.out.println(file.createNewFile()); //对文件重命名 File file = new File("d:\\a.txt"); File f = new File("a.txt"); File f1 = new File("hello\\hello.txt"); System.out.println(file.renameTo(f));//将file表示文件重命名为f时,必须保证file是存在的文件 System.out.println(f.renameTo(f1));
-
代码实现:
public class FileUsageDemo02 { public static void main(String[] args) { String string = "C:\\Users\\Administrator\\Desktop\\HZ‐J2ee1709\\Day16"; method1(string); method2(string); } //需求一:列出指定目录下所有子文件夹以及子文件 public static void method1(String path) { File file = new File(path); //list //子文件或者子文件夹的名称 String[] arr = file.list(); for(String str:arr) { System.out.println(str); } } /** * 需求二:列出指定目录下所有子文件夹以及子文件,要求格式如下: * 子文件: isFile() * 。。。。 * 子文件夹:isDirectory() * 。。。。 */ public static void method2(String path) { File file = new File(path); //获取指定路径下所有File对象 //listFiles() File[] files = file.listFiles(); for(File f:files) { if(f.isFile()) { System.out.println("子文件:"); //获取每个文件的名字 System.out.println(f.getName()); } else if(f.isDirectory()) { System.out.println("子文件夹:"); System.out.println(f.getName()); } } } //需求:列出指定目录下所有后缀为.java的文件 public static void method3(String path) { //endsWith File file = new File(path); String[] arr = file.list(); for(String str:arr) { if(str.endsWith(".java")) { System.out.println(str); } } } }
-
递归删除文件夹
package com.qf.day17; import java.io.File; import java.lang.reflect.Field; /** * 1 列出文件夹中所有的子文件夹和文件,包括子文件夹中文件和文件夹 * @author wgy * */ public class Demo3 { public static void main(String[] args) { //listFiles(new File("d:\\图片")); deleteDir(new File("c:\\")); } //1 列出文件夹中所有的子文件夹和文件,包括子文件夹中文件和文件夹 public static void listFiles(File dir) { System.out.println(dir.getAbsolutePath()); File[] files=dir.listFiles(); if(files!=null&&files.length>0) { for (File file : files) { if(file.isDirectory()) { //递归 listFiles(file); }else { System.out.println(file.getAbsolutePath()); } } } } //2递归删除文件夹 public static void deleteDir(File dir) { File[] files=dir.listFiles(); if(files!=null&&files.length>0) { for (File file : files) { if(file.isDirectory()) { deleteDir(file); }else { //删除文件 System.out.println(file.toString()+"‐‐‐‐‐"+file.delete()); } } } //删除文件夹 System.out.println(dir.toString()+"*********"+dir.delete()); } }
第二节 IO流
-
流的作用和原理
流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质 是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作
-
字节输入流
InputStream是一个抽象类,不能实例化对象。
-
文件输入流
FileInputStream
public class DemoFileInputStream { public static void main(String[] args) { //创建被操作文件:此文件必须存在,否则读取时,抛出文件找不到异常 File file = new File("test\\hello.txt"); //声明流,不初始化 FileInputStream fis = null; try { //初始化流 fis = new FileInputStream(file); //准备数组用来存储数据 byte[] b = new byte[3]; //先定义一个变量,初始值是‐1 int i = ‐1; //定义一个计数循环的变量 int count = 0; //while循环读取 while((i=fis.read(b))!=‐1) { count++; for(int j=0; j<i; j++) { System.out.print((char)b[j]); } } System.out.println(count);//计数:计算循环读取的次数 } catch (FileNotFoundException e) { // TODO Auto‐generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto‐generated catch block e.printStackTrace(); } finally { if(fis!=null) { try { fis.close(); } catch (IOException e) { // TODO Auto‐generated catch block e.printStackTrace(); } } } } }
-
字节输出流
OutputStream
-
文件输出流
FileOutputStream
public class TestFileOutputStream { public static void main(String[] args) { //向文件中写入数据 //在工程中创建一个test文件夹 //设计程序,向test\\hello.txt中写入hello world //第一步:创建被操作文件对象 //当向一个文件中写入数据时,若文件不存在,程序会自动创建 File file = new File("test\\hello.txt"); FileOutputStream fos = null; try { //第二步:创建流对象 fos = new FileOutputStream(file, true); //第三步:准备数据 String str = "hello world"; byte[] b = str.getBytes(); System.out.println(b.length); //第四步:使用流写入 fos.write(b); }catch(IOException e) { e.printStackTrace(); } finally { if(fos!=null) { try { //第五步:刷新流,关闭流 fos.flush(); fos.close(); } catch (IOException e) { // TODO Auto‐generated catch block e.printStackTrace(); } } } } }
-
字符输入流
Reader类:是所有字符输入流的父类,为一个抽象类,不能实例化对象,使用它的子类FileReader类
public class FileReaderUsageDemo { public static void main(String[] args) { //1.将文件的路径转换为File对象 File file = new File("file/input1.txt"); Reader reader = null; try { //2.实例化一个FileReader的对象 reader = new FileReader(file); //3.定义一个数组 char[] arr = new char[8]; //4.定义一个int的变量 int hasRead = 0; //5.循环读取 while((hasRead = reader.read(arr)) != ‐1) { String result = new String(arr, 0, hasRead); System.out.println(result); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { //避免出现空指针异常 if(reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
-
字符输出流
Writer:是所有字符输出流的父类,为一个抽象类,不能实例化对象,使用它的子类FileWriter类
public class FileWriterUsageDemo { public static void main(String[] args) throws IOException { File file = new File("file/output1.txt"); Writer writer = new FileWriter(file); //写入 //注意:区别于字节流,可以直接传字符串 writer.write("天边最美的云彩"); writer.flush();//立即写入硬盘 writer.close(); } }
第三节 扩展
-
使用字节流复制文件
// 1 使用字节流复制文件 public static void copy1() throws Exception{ //1创建字节文件输入输出流 InputStream is=new FileInputStream("d:\\003.jpg"); OutputStream os=new FileOutputStream("d:\\004.jpg"); //2读、写 byte[] buf=new byte[1024*4]; int len=0; while((len=is.read(buf))!=‐1){ //buf缓冲区,0从第一个位置写,len写的长度 os.write(buf,0,len); } //3关闭 is.close(); os.close(); System.out.println("复制完成"); } //2使用字符流复制文件 public static void copy2() throws Exception{ //1创建字符输入输出流 Reader reader=new FileReader("d:\\003.jpg"); Writer writer=new FileWriter("d:\\005.jpg"); //2读写 char[] buf=new char[1024*4]; int len=0; while((len=reader.read(buf))!=‐1){ writer.write(buf, 0, len); } //3关闭 reader.close(); writer.close(); System.out.println("复制完成"); }
第四节 小结
1 File类,表示硬盘上的文件或文件夹
1>创建对象,文件或文件夹不一定存在
2>判断是否存在 exists();
3>把文件或文件夹创建出来, createNewFile() mkdir() mkdirs();
4>删除 delete(); deleteOnExit();
5>获取 绝对路径、规范路径、文件或文件夹名、最后修改时间
6>判断是文件还是文件夹 isDirectory() isFile() isHidden
7>遍历文件夹中内容 list()
8>判断文件的可执行、可读取、可写入
2>1递归显示文件夹中所有文件、2递归删除
3IO流
流‐‐‐》管道
分类:
按照方向分:输入流和输出流
按照读取的字节个数:字节流和字符流
按照功能分:节点流 (负责读写数据)、处理流 (封装)
流的使用
字节输入流 InputStream‐‐‐‐‐>FileInputStream
字节输出流 OutputStream‐‐‐‐‐>FileOutputStream
字符输入流 Reader‐‐‐‐‐>InputStreamReader‐‐‐‐>FileReader
字符输出流 Writer‐‐‐‐‐>OutputStreamWriter‐‐‐>FileWriter
第十八天 缓冲流等
第一节 转换流
-
InputStreamReader:字节字符转换输入流,将字节输入流转换为字符输入流
public class InputStreamReaderDemo { public static void main(String[] args) throws IOException { //1.实例化File的对象 //File file = new File("file/input1.txt"); //2.实例化转换输入流的对象 //注意:当一个流的存在的意义是为了实例化另外一个流,则这个流不需要手动进行关闭 //InputStream input = new FileInputStream(file); //InputStreamReader reader = new InputStreamReader(input); //使用默认的字符集【GBK】进行实例化转换流 //InputStreamReader reader = new InputStreamReader(new FileInputStream(new File("file/input1.txt"))); //使用指定字符集进行实例化转换流 //字符集一般使用字符串直接传参,不区分大小写,但是,如果字符集书写有误的话,则会跑出 java.io.UnsupportedEncodingException InputStreamReader reader = new InputStreamReader(new FileInputStream(new File("file/input1.txt")),"UTF‐8"); //3.读取 char[] arr = new char[16]; int len = 0; while((len = reader.read(arr)) != ‐1) { String string = new String(arr, 0, len); System.out.println(string); } reader.close(); } }
-
OutputStreamWriter:字符转换输出流,将内存中的字符转成字节保存到硬盘中。
public class OutputStreamWriterDemo { public static void main(String[] args) throws IOException { //需求:将一段文本以utf‐8的格式写入到文件中【注,文件格式为默认格式】 //1.实例化FIle对象 //注意:对于所有的输出流而言,文件可以不存在,在进行写入的过程中可以自动进行创建 //但是,对于所有的输入流而言,文件必须先存在,然后才能操作,否则,会抛出 FileNotFounedException File file = new File("file/output1.txt"); //2.实例化转换输出流 //如果不想覆盖源文件中的内容时,则在传参的时候,设置一个参数为true OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file,true), "utf‐8"); //3.写入 writer.write("家客户放假啊刚回家"); //4.刷新 writer.flush(); //5.关闭 writer.close(); } }
第二节 缓冲流
-
BufferedInputStream类
2.2 BufferedOutputStream类 public class BufferedInputStreamDemo { public static void main(String[] args) throws IOException { //实例化一个File对象 File file = new File("file/test22.txt"); //实例化一个缓冲字节输入流的对象 BufferedInputStream input = new BufferedInputStream(new FileInputStream(file)); /* //读取 byte[] arr = new byte[1024]; int len = 0; while((len = input.read(arr)) != ‐1) { String string = new String(arr, 0, len); } */ byte[] arr = new byte[4]; int len = input.read(arr); String string = new String(arr, 0, len); System.out.println(string); input.mark(66); len = input.read(arr); string = new String(arr, 0, len); System.out.println(string); // 实现了效果:覆水可收 input.reset(); len = input.read(arr); string = new String(arr, 0, len); System.out.println(string); input.close(); } }
-
BufferedOutputStream类
public class BufferedOutputStreamDemo { public static void main(String[] args) throws IOException { //实例化FIle对象 File file = new File("test33.txt"); //实例化换种字节输出流 BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(file)); //写 output.write("你好的halle".getBytes()); //刷新 output.flush(); //关闭 output.close(); } }
-
BufferedReader类
public class BufferedReaderDemo { public static void main(String[] args) throws IOException { //实例化FIle对象 File file = new File("test33.txt"); //实例化缓冲字符流的对象 BufferedReader reader = new BufferedReader(new FileReader(file)); //方式一:read循环读取 /* //读取 char[] arr = new char[8]; int len = 0; while((len = reader.read(arr)) != ‐1) { String string = new String(arr, 0, len); } */ //方式二:readLine循环读取 /* String result1 = reader.readLine(); System.out.println(result1); String result2 = reader.readLine(); System.out.println(result2); */ String result = ""; while((result = reader.readLine()) != null) { System.out.println("第一行:" + result); } reader.close(); } }
-
BufferedWriter类
public class BufferedWriterDemo { public static void main(String[] args) throws IOException { // 实例化FIle对象 File file = new File("test33.txt"); //实例化缓冲字符输出流 BufferedWriter writer = new BufferedWriter(new FileWriter(file,true)); // 写 writer.write("今天天气还可以"); // 作用:主要就是为了换行 writer.newLine(); // 刷新 writer.flush(); //关闭 writer.close(); } }
第三节 内存流
- ByteArrayInputStream:将内容写入到内存中,是Inputstream的子类 ByteArrayOutputStream:将内存中数据输出,是OutputStream的子类
- 案例:完成一个字母大小写转换的程序
public class TextDemo02 {
public static void main(String[] args) throws IOException {
//定义一个字符串,全部由大写字母组成
String string = "HELLOWORLD";
//内存输入流
//向内存中输出内容,注意:跟文件读取不一样,不设置文件路径
ByteArrayInputStream bis = new ByteArrayInputStream(string.getBytes());
//内存输出流
//准备从内存中读取内容,注意:跟文件读取不一样,不设置文件路径
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int temp = 0;
//read()方法每次只读取一个字符
while((temp = bis.read()) != ‐1) {
//将读取的数字转为字符
char c = (char)temp;
//将字符变为大写
bos.write(Character.toLowerCase(c));
}
//循环结束之后,所有的数据都在ByteArrayOutputStream中
//取出内容,将缓冲区内容转换为字符串
String newString = bos.toString();
//关闭流
bis.close();
bos.close();
System.out.println(newString);
}
}
第四节 标准输入输出流
public class PrintStreamDemo {
public static void main(String[] args) throws FileNotFoundException {
//System.out.println("hello world");
//创建打印流的对象
//注意:默认打印到控制台,但是,如果采用setOut方法进行重定向之后,将输出到指定的文件中
PrintStream print = new PrintStream(new FileOutputStream(new File("test33.txt")));
/*
* static void setErr(PrintStream err)
重新分配“标准”错误输出流。
static void setIn(InputStream in)
重新分配“标准”输入流。
static void setOut(PrintStream out)
重新分配“标准”输出流。
* */
//将标准输出重定向到print的输出流
System.setOut(print);
System.out.println("hello world");
}
}
public class InputStreamDemo {
public static void main(String[] args) throws FileNotFoundException {
FileInputStream inputStream = new FileInputStream(new File("test33.txt"));
//setIn
System.setIn(inputStream);
//System.out.println("请输入内容:");
//默认情况下是从控制台进行获取内容
//但是如果使用setIn方法设置了重定向之后,将从指定文件中获取内容
Scanner sc = new Scanner(System.in);
String string = sc.next();
System.out.println(string);
}
}
第五节 对象流
流中流动的数据是对象 将一个对象写入到本地文件中,被称为对象的序列化 将一个本地文件中的对象读取出来,被称为对象的反序列化 使用对象流 ObjectInputStream: 对象输出流 ObjectOutputStream:对象输入流
注意:
一个对象流只能操作一个对象,如果试图采用一个对象流操作多个对象的话,会出现EOFException【文件意 外达到了文件末尾
如果向将多个对象序列化到本地,可以借助于集合,【思路:将多个对象添加到集合中,将集合的对象写入到 本地文件中,再次读出来,获取到的仍然是集合对象,遍历集合】
对象中那些字段可以不序列化: 1 transient 修饰的字段 2 静态的字段 在要序列化类中添加字段,保证序列化和反序列化是同一个类 private static final long serialVersionUID = 100L;
public class ObjectStreamDemo {
public static void main(String[] args) {
// TODO Auto‐generated method stub
//objectOutputStreamUsage();
objectInputStreamUsage();
}
// 写:将对象进行序列化
public static void objectOutputStreamUsage() {
//1.实例化一个Person的对象
Person person = new Person("张三", 10, 'B');
//2.实例化一个对象输出流的对象
ObjectOutputStream output = null;
try {
output = new ObjectOutputStream(new FileOutputStream(new File("file/person.txt")));
//3.将对象写入到流中
output.writeObject(person);
//4.刷新
output.flush();
} catch (FileNotFoundException e) {
// TODO Auto‐generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto‐generated catch block
e.printStackTrace();
}
finally {
try {
output.close();
} catch (IOException e) {
// TODO Auto‐generated catch block
e.printStackTrace();
}
}
}
// 读:反序列化
public static void objectInputStreamUsage() {
//1.实例化对象输入流的对象
try {
ObjectInputStream input = new ObjectInputStream(new FileInputStream(new
File("file/person.txt")));
//2.读取
Object object = input.readObject();
}
}
第六节 RandomAccessFile类
RandomAccessFile是用来访问那些保存数据记录的文件的,你就可以用seek( )方法来访问记录,并进行读写了。这些记 录的大小不必相同;但是其大小和位置必须是可知的。但是该类仅限于操作文件。
-
案例一:RandomAccessFile类的应用
public class TextDemo01 { public static void main(String[] args) throws Exception { RandomAccessFile file = new RandomAccessFile("file.txt", "rw"); // 以下向file文件中写数据 file.writeInt(20);// 占4个字节 file.writeDouble(8.236598);// 占8个字节 //这个长度写在当前文件指针的前两个字节处,可用readShort()读取 file.writeUTF("这是一个UTF字符串"); file.writeBoolean(true);// 占1个字节 file.writeShort(395);// 占2个字节 file.writeLong(2325451l);// 占8个字节 file.writeUTF("又是一个UTF字符串"); file.writeFloat(35.5f);// 占4个字节 file.writeChar('a');// 占2个字节 //把文件指针位置设置到文件起始处 file.seek(0); // 以下从file文件中读数据,要注意文件指针的位置 System.out.println("——————从file文件指定位置读数据——————"); System.out.println(file.readInt()); System.out.println(file.readDouble()); System.out.println(file.readUTF()); //将文件指针跳过3个字节,本例中即跳过了一个boolean值和short值。 file.skipBytes(3); System.out.println(file.readLong()); //跳过文件中“又是一个UTF字符串”所占字节 //注意readShort()方法会移动文件指针,所以不用写2。 file.skipBytes(file.readShort()); System.out.println(file.readFloat()); // 以下演示文件复制操作 System.out.println("——————文件复制(从file到fileCopy)——————"); file.seek(0); RandomAccessFile fileCopy = new RandomAccessFile("fileCopy.txt", "rw"); int len = (int) file.length();// 取得文件长度(字节数) byte[] b = new byte[len]; //全部读取 file.readFully(b); fileCopy.write(b); System.out.println("复制完成!"); } }
第七节 Properties类
是Map接口的一个实现类,并且是Hashtable的子类
Properties集合中元素也是以键值对的形式存在的
Properties特点: 1 存储属性名和属性值 2 属性名和属性值都是字符串 3 和流有关系 4 没有泛型
public class PropertiesDemo {
public static void main(String[] args) {
//1.实例化一个Properties的对象
Properties pro = new Properties();
System.out.println(pro);
//2.把文件userlist.properties中的键值对同步到集合中
//实质:读取
/**
* void load(InputStream inStream)
从输入流中读取属性列表(键和元素对)。
*/
try {
pro.load(new BufferedInputStream(new FileInputStream(new
File("file/userlist.properties"))));
} catch (FileNotFoundException e) {
// TODO Auto‐generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto‐generated catch block
e.printStackTrace();
}
System.out.println(pro);
//3.向集合中添加一对键值对
/*
* Object setProperty(String key, String value)
调用 Hashtable 的方法 put。
* */
pro.setProperty("address", "china");
System.out.println(pro);
try {
//4.store
//实质:写入
//comments:工作日志
pro.store(new BufferedOutputStream(new FileOutputStream(new
File("file/userlist.properties"))), "add a pair of key and value");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
第八节 装饰者设计模式
-
概念:
装饰模式指的是在不必改变原类文件和继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对 象,也就是装饰来包裹真实的对象。
-
应用场景:
需要扩展一个类的功能,或给一个类添加附加职责。
-
优点:
耦合性低,提高重用性
-
代码实现:
/** * 抽象人类 * @author wgy * */ public abstract class AbstractPerson { public abstract void eat(); } /** *子类1 */ public class Person extends AbstractPerson { String name; public void eat() { System.out.println(name+"正在吃东西........."); } } package com.qf.day18_6; /** * 子类2 * @author wgy * */ public class Person2 extends AbstractPerson{ String name; @Override public void eat() { System.out.println(name+"吃......"); } } package com.qf.day18_6; import java.io.BufferedInputStream; /** * 增强Person * @author wgy * */ public class StrongPerson extends AbstractPerson { //使用person创建一个变量 AbstractPerson p; public StrongPerson(AbstractPerson p) { this.p = p; } public void eat() { p.eat(); System.out.println("抽一口"); System.out.println("眯一会"); System.out.println("写会java代码"); } } //测试类 package com.qf.day18_6; public class Test { public static void main(String[] args) { Person benwei=new Person(); benwei.name="本伟"; Person2 zhengshuai=new Person2(); zhengshuai.name="郑帅"; StrongPerson strongPerson=new StrongPerson(benwei); StrongPerson strongPerson2=new StrongPerson(zhengshuai); strongPerson.eat(); strongPerson2.eat(); } }
第十九天 多线程基础
第一节 进程和线程
-
进程(Process)
1 正在运行的程序,是一个程序的运行状态和资源占用(内存,CPU)的描述,通过进程ID区分。 2 进程是程序的一个动态过程,它指的是从代码加载到执行完毕的一个完成过程。 3 目前操作系统支持多进程多任务。
进程的特点: a.独立性:不同的进程之间是独立的,相互之间资源不共享(举例:两个正在上课的教室有各自的财 产,相互之间不共享) b.动态性:进程在系统中不是静止不动的,而是在系统中一直活动的 c.并发性:多个进程可以在单个处理器上同时进行,且互不影响
-
线程
1 线程就是一条执行路径。是进程的组成部分,一个进程可以有多个线程,每个线程去处理一个特定的子任务。
线程的特点: 线程的执行是抢占式的,多个线程在同一个进程中可以并发执行,其实就是CPU快速的在不同的线程之间切换, 也就是说,当前运行的线程在任何时候都有可能被挂起,以便另外一个线程可以运行
-
进程和线程的关系以及区别
a.一个程序运行后至少有一个进程 b.一个进程可以包含多个线程,但是至少需要有一个线程,否则这个进程是没有意义的 c.进程间不能共享资源,但线程之间可以 d.系统创建进程需要为该进程重新分配系统资源,而创建线程则容易的多,因此使用线程实现多任务并发比多进程的效率 高
第二节 多线程的实现
-
创建方式有三种
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
-
两种实现方式的比较
实现Runnable接口的方式 a.资源类实现了Runnable接口。如果资源类有多个操作,需要把功能提出来,单独实现Runnable接口。 b.可以多个线程共享同一个资源,所以非常适合多个线程来处理同一份资源的情况 c.弊端:编程稍微复杂,不直观,如果要访问当前线程,必须使用Thread.currentThread()
继承Thread类的方式 a.没有资源共享,编写简单,如果要访问当前线程,除了可以通过Thread.currentThread()方式之外,还可以 使用getName()获取线程名字。 b.弊端:因为线程类已经继承了Thread类,则不能再继承其他类【单继承】
实际上大多数的多线程应用都可以采用实现Runnable接口的方式来实现【推荐使用匿名内部类】
-
调用start()与run()方法的区别
当调用start()方法时将创建新的线程,并且执行run()方法里的代码,但是如果直接调用run()方法,不会创 建新的线程。
-
使用Callable接口实现多线程
public class MyCallable implements Callable<Integer>{ @Override public Integer call() throws Exception { int sum=0; for(int i=1;i<=100;i++){ Thread.sleep(100); sum+=i; } System.out.println("子线程执行了........."+Thread.currentThread().getName()); //System.out.println("子线程执行了........."+Thread.currentThread().getName()); return sum; } } public static void main(String[] args) throws Exception{ //1创建Mycallable对象(可调用的) MyCallable callable=new MyCallable(); //2创建一个任务实现Runable接口 FutureTask<Integer> task=new FutureTask<Integer>(callable); //3创建线程对象 Thread thread=new Thread(task); //4启动 thread.start(); //5获取返回值 Integer sum=task.get();//会等待子线程执行完毕,返回结果 System.out.println(sum); }
第三节 线程的常用方法
-
设置线程优先级
优先级范围1~10,默认为5,对应的数值越大,说明优先级越高,这个方法的设置一定要在start之前
-
合并(加入)线程
在执行原来线程的过程中,如果遇到了合并线程,则优先执行合并进来的线程,执行完合并进来的线程后,再回 到原来的任务中,继续执行原来的线程
特点: a.线程合并,当前线程一定会释放cpu时间片,cpu会将时间片分给要Join的线程 b.哪个线程需要合并就在当前线程中,添加要合并的线程 c.join之前,一定要将线程处于准备状态start
-
代码实现:
package com.qf.day19_9; public class JoinThread extends Thread{ @Override public void run() { for(int i=0;i<50;i++) { System.out.println(Thread.currentThread().getName()+"....."+i); try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto‐generated catch block e.printStackTrace(); } } } } public static void main(String[] args) throws Exception { //1创建线程对象 JoinThread joinThread=new JoinThread(); //2启动 joinThread.start(); //加入线程(阻塞了主线程,等join执行完完毕才继续执行) joinThread.join(); //3for for(int i=0;i<30;i++) { System.out.println("主线程‐‐‐‐‐‐‐‐‐‐‐‐‐‐"+i); Thread.sleep(20); } }
-
后台线程
线程分为前台(用户)线程和后台(守护)线程。 后台线程:隐藏起来一直在默默运行的线程,直到进程结束,又被称为守护线程,JVM的垃圾回收线程就是典型 的后台线程。特征:如果所有的前台线程都死亡,后台线程会自动死亡。 前台线程:默认的线程都是前台线程,如果前台不执行完毕,程序不会退出。
package com.qf.day19_10;
public class DeamonThread extends Thread {
@Override
public void run() {
for(int i=0;i<100;i++) {
System.out.println(Thread.currentThread().getName()+"...."+i);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto‐generated catch block
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws Exception{
//1创建线程对象
DeamonThread deamonThread=new DeamonThread();
//设置线程为后台线程
deamonThread.setDaemon(true);
//2启动线程
deamonThread.start();
for(int i=0;i<20;i++) {
System.out.println("主线程:"+i);
Thread.sleep(20);
}
}
-
线程让步
可以让当前正在执行的线程暂停,但它不会阻塞该线程,他只是将该线程转入就绪状态,完全可能出现的情况 是:当某个线程调用了yield方法暂停之后,线程调度器又将其调度出来重新执行
实际上,当某个线程调用了yield方法暂停之后,只有优先级与当前线程相同,或者优先级比当前线程更高的就 绪状态的线程才会获得执行的机会。
代码实现:
public class YieldFunctionDemo01 {
public static void main(String[] args) {
YieldThread t0 = new YieldThread("线程000");
//t0.setPriority(8);
t0.start();
YieldThread t1 = new YieldThread("线程111");
t1.start();
}
}
class YieldThread extends Thread {
public YieldThread(){}
public YieldThread(String name) {
super(name);
}
@Override
public void run() {
for(int i = 0;i < 50;i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if(i==20) {
//线程让步,不会让线程进入阻塞状态
Thread.yield();
}
}
}
}
-
线程中断 interrupt()
程序在等待过程中,可以使用interrupt方法打断
第四节 线程的生命周期
- 线程五状态
-
线程七状态
小结
1 进程:正在运行的程序,操作系统通过进程Id区分不同进程。
2 线程:进程中的一条执行路径
3 区别: a.一个程序运行后至少有一个进程 b.一个进程可以包含多个线程,但是至少需要有一个线程,否则这个进程是没有意义的 c.进程间不能共享资源,但线程之间可以 d.系统创建进程需要为该进程重新分配系统资源,而创建线程则容易的多,因此使用线程实现多任务并发比多进 程的效率高 4 多线程的创建 (1)继承Thread ,重写run方法 (2)实现Runnable接口 (3)实现Callable接口 有返回值,可以抛出异常 5 线程的方法
线程对象.setName(); Thread.currentThread().getName(); Thread.sleep(); 线程对象.priority(); 线程对象.join(); 线程对象.setDaemone(); Thread.yield(); 线程对象.interrupt();
6声明周期 五个状态 新生‐‐‐>就绪‐‐‐‐>运行‐‐‐‐>阻塞‐‐‐‐‐>死亡
第二十天 多线程同步
第一节 多线程访问临界资源
-
同步代码块
同步:Synchronized:有等待 异步:Asynchronized:没有等待,各执行各的
-
ReentrantLock类(可重入锁)jdk1.5
public class BreadContainer { private Bread[] breads = new Bread[6]; private int index = -1; private Lock lock = new ReentrantLock(); //创建两个Condition条件队列 Condition pro_condition = lock.newCondition(); Condition con_conditon = lock.newCondition();//Condition 状态 //存入面包 public void input(Bread b){ lock.lock(); try{ while(index >= 5){ try { pro_condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } } index++; breads[index] = b; System.out.println(Thread.currentThread().getName() + "生成了" + b.getId()); con_conditon.signal();//唤醒 }finally { lock.unlock(); } } public void output(Bread b){ lock.lock(); try { while (index < 0){ try { con_conditon.await(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + "消费了" + b.getId()); breads[index--] = null; pro_condition.signal(); }finally { lock.unlock(); } } }
第二节 死锁
-
死锁的条件: 1两个以上的线程 2至少两个锁以上 3同步中嵌套同步
/* *锁 */ public class Lock { public static Object locka=new Object();//第一个锁 public static Object lockb=new Object();//第二个锁 } /* *男孩 */ public class Boy extends Thread{ @Override public void run() { while (true) { synchronized (Lock.locka) { System.out.println("男孩拿着locka"); synchronized (Lock.lockb) { System.out.println("男孩拿到lockb"); System.out.println("男孩可以吃了...."); } } } } } /* * 女孩 */ public class Girl extends Thread{ @Override public void run() { while (true) { synchronized (Lock.lockb) { System.out.println("女孩拿着lockb"); synchronized (Lock.locka) { System.out.println("女孩拿到了locka"); System.out.println("女孩可以吃了..."); } } } } } public static void main(String[] args) { Boy shaqiang=new Boy(); Girl xiaofeng=new Girl(); shaqiang.start(); xiaofeng.start(); }
第三节 多线程在单例中的应用
volatile:作用1 保证可见性 作用2限制代码重排序
synchronized: 作用1 保证原子性(互斥性) 作用2 保证可见性
public class SingleTon {
private SingleTon(){
//禁止反射破解
synchronized (SingleTon.class){
if(instance != null){
throw new RuntimeException("不能使用反射创建对象!");
}
}
}
private static volatile SingleTon instance;//volatile,易挥发的,保证线程可见性,禁止指令重排序
public static SingleTon getInstance(){
if(instance == null){//检查,加速
synchronized (SingleTon.class){
if (instance == null){
instance = new SingleTon();
}
}
}
return instance;
}
}
/**
* @Auther: xbh
* @Date: 2019/8/10 16:18
* @Description:
* 静态内部类单例
* 好处:
* 1、节省空间
* 2、不会出现线程安全问题
*/
public class SingleTon2 {
private SingleTon2(){
if(Holder.INSTANCE != null){
throw new RuntimeException("不能使用反射创建对象!");
}
}
static class Holder{
private static final SingleTon2 INSTANCE = new SingleTon2();
}
public static SingleTon2 getInstance(){
return Holder.INSTANCE;
}
}
/**
* @Auther: xbh
* @Date: 2019/8/10 16:22
* @Description:
* 枚举写法
* 好处
* 1、无线程安全问题
* 2、不会被反射破解
* 缺点
* 2、浪费空间
*/
public enum SingleTon3 {
INSTANCE;
public static SingleTon3 getInstance(){
return INSTANCE;
}
}
第四节 线程的通信【生产者与消费者设计模式】
wait(): 等待,线程执行这个方法进入等待队列(和锁有关,一个锁对应一个等待队列), 需要被唤醒 notify(): 通知唤醒,从等待队列中随机唤醒一个线程 notifyAll():全部唤醒,把等待队列中所有的线程都唤醒
public class BankCard {
private double money;
private boolean flag;// 标记 true 表示有钱, false没钱
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
/***
* 存取
*/
public synchronized void save() { //this
while(flag) {//true 有钱
try {
this.wait();//等待,释放了cpu,释放了锁 ,调用wait的对象是锁
} catch (InterruptedException e) {
// TODO Auto‐generated catch block
e.printStackTrace();
}//等待
}
money=money+1000;
System.out.println(Thread.currentThread().getName()+"存了1000,余额是:"+money);
flag=true;//修改标记
this.notifyAll();//唤醒取钱线程取取钱
}
/**
* 取
*/
public synchronized void qu() {//this
while(flag==false) {
try {
this.wait();//等待 释放cpu和锁
} catch (InterruptedException e) {
// TODO Auto‐generated catch block
e.printStackTrace();
}
}
money=money‐1000;
System.out.println(Thread.currentThread().getName()+"取了1000,余额是:"+money);
//修改标记
flag=false;
//唤醒
this.notifyAll();
}
}
package com.qf.day20_8;
/**
* 存取类
* @author wgy
*
*/
public class AddMoney implements Runnable{
private BankCard card;
public AddMoney(BankCard card) {
this.card = card;
}
@Override
public void run() {
for(int i=0;i<10;i++) {
card.save();
}
}
}
package com.qf.day20_8;
public class SubMoney implements Runnable{
private BankCard card;
public SubMoney(BankCard card) {
this.card = card;
}
@Override
public void run() {
for(int i=0;i<10;i++) {
card.qu();
}
}
}
package com.qf.day20_8;
public class Test {
public static void main(String[] args) {
//1创建卡
BankCard card=new BankCard();
//2创建存钱和取钱功能
AddMoney add=new AddMoney(card);
SubMoney sub=new SubMoney(card);
//3创建线程对象
Thread zhengshuai=new Thread(add,"帅帅");
Thread dalang=new Thread(add, "大郎");
Thread benwei=new Thread(sub,"本伟");
Thread xiaolian=new Thread(sub,"金莲");
//4启动
zhengshuai.start();
benwei.start();
dalang.start();
xiaolian.start();
}
}
画图分析
扩展知识1 读写锁
ReadWriteLock接口:可以实现多个读线程同时读取数据,写线程需要互斥执行。
读|写 、写|写 需要互斥
读|读 不需要互斥
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteDemo {
private int number=0;
private ReadWriteLock lock=new ReentrantReadWriteLock();
public void read() {
lock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"读取了:"+number);
} finally {
lock.readLock().unlock();
}
}
public void write(int number) {
lock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"写入了"+number);
this.number=number;
} finally {
lock.writeLock().unlock();
}
}
}
import java.util.Random;
public class Test {
public static void main(String[] args) {
ReadWriteDemo rw=new ReadWriteDemo();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
rw.write(new Random().nextInt(100));
}
}).start();
Runnable r=new Runnable() {
@Override
public void run() {
rw.read();
}
};
for(int i=0;i<100;i++) {
new Thread(r).start();
}
}
}
扩展知识2:线程池
为什么需要线程池:
例如有非常的多的任务需要多线程来完成,且每个线程执行时间不会太长,这样会频繁的创建和销毁线程。频繁创建和销毁线程会比较耗性能。如果有了线程池就不要创建更多的线程来完成任务,因为线程可以重用。
线程池用维护者一个队列,队列中保存着处于等待(空闲)状态的线程。不用每次都创建新的线程。
和线程池相关的接口和类存在java.util.concurrent并发包中。
接口:
1 Executor:线程池的核心接口,负责线程的创建使用和调度的根接口
2 ExecutorService: Executor的子接口,线程池的主要接口, 提供基本功能。
3 ScheduledExecutorService: ExecutorService的子接口,负责线程调度的子接口。
实现类:
1 ThreadPoolExecutor:ExecutorService的实现类,负责线程池的创建使用。
2 ScheduledThreadPoolExecutor:继承 ThreadPoolExecutor,并实现 ScheduledExecutorService接口,既有线程池的功能,又具有线程调度功能。
3 Executors:线程池的工具类,负责线程池的创建。
newFixedThreadPool();创建固定大小的线程池。
newCachedThreadPool();创建缓存线程池,线程池大小没有限制。根据需求自动调整线程数量。
newSingleThreadExecutor();创建单个线程的线程池,只有一个线程。
newScheduledThreadPool();创建固定大小的线程池,可以延迟或定时执行任务。
案例一:使用线程池实现卖票
public class Ticket implements Runnable{
private int ticket=100;
@Override
public void run() {
while(true) {
if(ticket<=0) {
break;
}
System.out.println(Thread.currentThread().getName()+"卖第"+ticket+"张票");
ticket--;
}
}
}
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test {
public static void main(String[] args) {
Ticket ticket=new Ticket();
ExecutorService threadPool = Executors.newFixedThreadPool(4);
for(int i=0;i<4;i++) {
threadPool.submit(ticket);
}
threadPool.shutdown();
System.out.println("主线程执行完毕........");
}
}
案例二:线程池计算1-100的和,要求采用Callable接口
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Test {
public static void main(String[] args) throws Exception {
ExecutorService threadPool = Executors.newFixedThreadPool(4);
List<Future<Integer>> list=new ArrayList<>();
for (int i = 0; i < 10; i++) {
Future<Integer> future = threadPool.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i <= 100; i++) {
Thread.sleep(10);
sum += i;
}
System.out.println(Thread.currentThread().getName() + "计算完毕");
return sum;
}
});
list.add(future);
}
threadPool.shutdown();
System.out.println("主线程结束了。。。。");
for (Future<Integer> fu : list) {
int s = fu.get();
System.out.println(s);
}
}
}
案例三:延迟执行任务
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Test2 {
public static void main(String[] args) throws Exception{
ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);
List<Future<Integer>> list=new ArrayList<>();
for(int i=0;i<10;i++) {
Future<Integer> future=threadPool.schedule(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int ran=new Random().nextInt(100);
System.out.println(Thread.currentThread().getName()+"...."+ran);
return ran;
}
},3,TimeUnit.SECONDS);
list.add(future);
}
threadPool.shutdown();
System.out.println("主线程结束了...........");
for (Future<Integer> future2 : list) {
int n=future2.get();
System.out.println(n);
}
}
}
扩展知识3:定时器Timer
public class Demo{
public static void main(String[] args) throws ExecutionException, InterruptedException {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format = simpleDateFormat.format(new Date());
Timer timer = new Timer();
TimerTask timerTask = new TimerTask(){
@Override
public void run() {
System.out.println("sss");
}
};
timer.schedule(timerTask, 2000, 1000);
}
}
小结
1 多线程访问临界资源 数据安全问题? 2 解决安全问题 同步 + 锁 3 同步代码块 synchronized(metux){
} 锁:引用类型 ,唯一的 4 同步方法 方法的返回值前面 synchronized 非静态方法 锁:this 静态方法 锁:类名.class
5 可重入锁 ReentrantLock Lock lock=new ReeentrantLock(); lock.lock() try{
}finally{
lock.unlock();
}
6 死锁
1多个锁 ,多个线程
2同步中嵌套同步
7 单例模式优化
懒汉式写法
8 线程通信
Object中三个方法
wait(); 等待
notifiy(); 唤醒
notifyAll();唤醒所有的
jdk1.5 Lock通信 Conditon代替监视器方法
第二十一天 网络编程
第一节 网络编程基础
- OSI参考模型:包括七层:[物理层]、[数据链路层]、[网络层]、[传输层]、[会话层]、[表示层]和[应用层]
- TCP/IP参考模型:包括四层: 1.网络访问层(数据链路层/物理层):包括操作系统中的设备驱动程序、计算机中对应的网络接口卡 2.互联网层:处理分组在网络中的活动,比如分组的选路。 3.传输层:主要为两台主机上的应用提供端到端的通信。 4.应用层(应用层/表示层/会话层):负责处理特定的应用程序细节。
第二节:计算机之间通信
- IP地址分类 A类:保留给政府结构,1.0.0.1 ~ 126.255.255.254 B类:分配给中型企业,128.0.0.1 ~ 191.255.255.254 C类:分配给任何需要的个人,192.0.0.1 ~ 223.255.255.254 D类:用于组播,224.0.0.1 ~ 239.255.255.254 E类:用于实验,240.0.0.1 ~ 255.255.255.254 回环地址:127.0.0.1,指本机,一般用于测试使用,使用ping命令测试: ping 127.0.0.1
- 常用端口: mysql:3306 oracle:1521 tomcat:8080 web服务器(http):80 ftp服务器:21 SMTP 25 POP3 110
第三节:相关类的使用
-
InetAddress类
//1.获取主机:主机名称和ip地址 /** * static InetAddress getLocalHost() 返回本地主机。 */ InetAddress id1 = null; try { id1 = InetAddress.getLocalHost(); //USER‐VG9EDR1SST/10.31.165.42 System.out.println(id1); } catch (UnknownHostException e) { // 未知的主机 e.printStackTrace(); } //2.获取ip地址的字符串表示形式 /** * String getHostAddress() 返回 IP 地址字符串(以文本表现形式)。 */ String str1 = id1.getHostAddress(); System.out.println(str1);//10.31.165.42 //3.获取主机名 /** * String getHostName() 获取此 IP 地址的主机名。 */ String str2 = id1.getHostName(); System.out.println(str2); //4.根据主机或者ip地址获取InetAddress对象 /** * static InetAddress getByName(String host) 在给定主机名的情况下确定主机的 IP 地址。 */ try { InetAddress id2 = InetAddress.getByName("10.31.165.42"); ///10.31.165.42 System.out.println(id2); InetAddress id3 = InetAddress.getByName("www.baidu.com"); //www.baidu.com/115.239.211.112 System.out.println(id3); } catch (UnknownHostException e) { e.printStackTrace(); } //5.根据主机或者ip地址获取所有InetAddress对象 /** * static InetAddress[] getAllByName(String host) */ try { InetAddress[] arr = InetAddress.getAllByName("www.baidu.com"); for(InetAddress address:arr) { //www.baidu.com/115.239.210.27 System.out.println(address.toString()); //115.239.210.27 System.out.println(address.getHostAddress()); //www.baidu.com System.out.println(address.getHostName()); } } catch (UnknownHostException e) { e.printStackTrace(); }
-
URLEncoder类和URLDecoder类
//%E5%8D%83%E9%94%8B //URLDecoder:解码,将特殊中文字符串转换为普通字符串 String string1 = URLDecoder.decode("%E5%8D%83%E9%94%8B", "utf‐8"); System.out.println(string1); //URLEncoder:编码,将普通字符串转换为特殊字符串 String string2 = URLEncoder.encode("Java的开发指南", "GBK"); System.out.println(string2); //注意:编码和解码需要采用相同的字符集 String string3 = URLDecoder.decode(string2, "utf‐8"); System.out.println(string3);
-
第四节:基于TCP的网络编程
-
基于TCP的Socket(套接字)通信模型
第五节:Socket和ServerSocket
-
客户端发送消息,服务端接收消息
/** * @Auther: xbh * @Date: 2019/8/12 16:19 * @Description: * 1.创建客户端套接字Socket,并制定服务器的ip和端口号 * 2.获取输入、输出流 * 3.发送数据 * 4.关闭 */ public class TcpClient { public static void main(String[] args) throws IOException { //1.创建客户端套接字Socket,并制定服务器的ip和端口号 Socket socket = new Socket("127.0.0.1", 10086); //获取输入、输出流 BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); //发送数据 writer.write("好久不见"); //关闭 writer.close(); } } /** * @Auther: xbh * @Date: 2019/8/12 16:13 * @Description: * TCP的服务器端 * 步骤 * 1.创建ServerSocket(服务器套接字对象) * 2.侦听或监听,返回Socket(客户端套接字) * 3.获取输入输出流 * 4.处理数据 * 5.关闭资源 */ public class TcpServer { public static void main(String[] args) throws IOException { //1.创建ServerSocket(服务器套接字对象) ServerSocket listener = new ServerSocket(10086); System.out.println("服务器已启动"); //2.侦听或监听,返回Socket(客户端套接字) Socket accept = listener.accept(); //3.获取输入输出流 BufferedReader br = new BufferedReader( new InputStreamReader(accept.getInputStream())); String s = br.readLine(); System.out.println("客户端说:" + s); br.close(); } }
-
客户端发送消息,服务端回复消息
/** * @Auther: xbh * @Date: 2019/8/12 16:37 * @Description: */ public class TcpClient { public static void main(String[] args) throws IOException { //创建socket Socket socket = new Socket("127.0.0.1", 10010); //获取输入输出流 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); //处理数据 writer.write("好久不见");//\n数据结束 writer.newLine();//刷新 writer.flush();//刷新缓冲 System.out.println("服务端说:" + bufferedReader.readLine()); bufferedReader.close(); writer.close(); socket.close(); } } /** * @Auther: xbh * @Date: 2019/8/12 16:33 * @Description: * Tcp服务器端 */ public class TcpServer { public static void main(String[] args) throws IOException { //创建服务器套接字 ServerSocket listener = new ServerSocket(10010); System.out.println("服务器已启动"); //监听,并返回客户端套接字 Socket accept = listener.accept(); //获取输入输出流 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(accept.getInputStream())); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream())); //处理数据 System.out.println("客户端说:" + bufferedReader.readLine());//readLine读到换行符才输出 writer.write("还好吗?"); writer.newLine();//刷新 writer.flush(); //关闭 bufferedReader.close(); writer.close(); accept.close(); listener.close(); } }
-
客户端上传文件到服务端【以图片为例】
/** * @Auther: xbh * @Date: 2019/8/12 16:57 * @Description: */ public class FileClient { public static void main(String[] args) throws IOException { //创建Socket Socket socket = new Socket("127.0.0.1", 8888); //获取输出流 OutputStream outputStream = socket.getOutputStream(); //读取文件并输出 FileInputStream fileInputStream = new FileInputStream("C:\\Users\\xbh\\Desktop\\测试\\Task5.PNG"); byte[] buf = new byte[1024]; int len = -1; while ((len = fileInputStream.read(buf)) != -1){ outputStream.write(buf); outputStream.flush(); } System.out.println("发送完毕"); outputStream.close(); fileInputStream.close(); socket.close(); } } /** * @Auther: xbh * @Date: 2019/8/12 16:57 * @Description: */ public class FileServer { public static void main(String[] args) throws IOException { //创建ServerSocket,并指定端口号 ServerSocket listener = new ServerSocket(8888); //监听,返回客户端套接字 System.out.println("服务器已启动"); Socket accept = listener.accept(); //获取输入流 InputStream inputStream = accept.getInputStream(); FileOutputStream fos = new FileOutputStream("D:\\aa.PNG"); byte[] buf = new byte[1024]; int len = -1; while ((len = inputStream.read(buf)) != -1){ fos.write(buf, 0, len); } System.out.println("接收完毕"); //关闭 fos.close(); inputStream.close(); accept.close(); listener.close(); } }
-
5.4 多个客户端和一个服务端通信【线程版本】
/** * @Auther: xbh * @Date: 2019/8/12 17:30 * @Description: */ public class ChatClient { public static void main(String[] args) throws IOException { //创建socket Socket socket = new Socket("10.9.21.146", 9999); //获取输出流 BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); //发送 Scanner input = new Scanner(System.in); while (true){ String data = input.next(); writer.write(data); writer.newLine(); writer.flush(); if (data.equals("886") || data.equals("over") || data.equals("baibai")){ break; } } //关闭 writer.close(); socket.close(); System.out.println("结束了!"); } } /** * @Auther: xbh * @Date: 2019/8/12 17:23 * @Description: */ public class ChatServer { public static void main(String[] args) throws IOException { //创建ServerSocket ServerSocket listener = new ServerSocket(9999); //监听 System.out.println("聊天服务器已经启动..."); try{ while (true){ Socket accept = listener.accept(); new ChatThread(accept).start(); } }catch (Exception e){ System.out.println("聊天室结束了"); }finally { listener.close(); } } } /** * @Auther: xbh * @Date: 2019/8/12 17:24 * @Description: */ public class ChatThread extends Thread { private Socket socket; public ChatThread(Socket socket) { this.socket = socket; } @Override public void run() { //接收数据 if (socket != null){ BufferedReader bufferedReader = null; try { bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); while (true){ String s = bufferedReader.readLine(); System.out.println(socket.getInetAddress().getHostAddress() + "说:" + s); if(s.equals("baibai") || s.equals("886") || s.equals("over")){ System.out.println(socket.getInetAddress().getHostAddress() + "退出了"); break; } } } catch (IOException e) { System.out.println(socket.getInetAddress().getHostAddress() + "异常退出了"); }finally { try { bufferedReader.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
-
TCP实现注册登录
/** * @Auther: xbh * @Date: 2019/8/13 10:08 * @Description: */ public class LoginThread extends Thread { private Properties properties; public LoginThread(Properties properties){ this.properties = properties; } @Override public void run() { BufferedReader br = null; BufferedWriter bw = null; ServerSocket listener = null; Socket socket = null; //注册 try { listener = new ServerSocket(7777); //监听 socket = listener.accept(); //接收客户端发送的数据 br = new BufferedReader(new InputStreamReader(socket.getInputStream())); bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); //接收数据 String data = br.readLine();//数据格式 "zhangsan#123456”用户名#密码 //处理数据 String[] infos = data.split("#"); if(infos != null){ String username = infos[0]; String password = infos[1]; //判断属性集合中是否存在 if (!properties.containsKey(username)){ bw.write("登陆失败"); bw.newLine(); bw.flush(); }else{ if(properties.containsValue(password)){ bw.write("登陆成功"); bw.newLine(); bw.flush(); }else{ bw.write("密码错误"); bw.newLine(); bw.flush(); } } } } catch (IOException e) { e.printStackTrace(); }finally { try { br.close(); bw.close(); listener.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * @Auther: xbh * @Date: 2019/8/13 10:08 * @Description: */ public class RegThread extends Thread { private Properties properties; public RegThread(Properties properties){ this.properties = properties; } @Override public void run() { BufferedReader br = null; BufferedWriter bw = null; ServerSocket listener = null; Socket socket = null; FileWriter fw = null; //注册 try { listener = new ServerSocket(6666); //监听 socket = listener.accept(); //接收客户端发送的数据 br = new BufferedReader(new InputStreamReader(socket.getInputStream())); bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); //接收数据 String data = br.readLine();//数据格式 "zhangsan#123456”用户名#密码 //处理数据 String[] infos = data.split("#"); if(infos != null){ String username = infos[0]; String password = infos[1]; //判断属性集合中是否存在 if (!properties.containsKey(username)){ properties.setProperty(username, password); bw.write("注册成功"); bw.newLine(); bw.flush(); fw = new FileWriter("userinfo.properties"); properties.store(fw,""); }else{ bw.write("用户名已存在"); bw.newLine(); bw.flush(); } } } catch (IOException e) { e.printStackTrace(); }finally { try { br.close(); bw.close(); listener.close(); socket.close(); fw.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * @Auther: xbh * @Date: 2019/8/13 10:49 * @Description: */ public class TcpClient { public static void main(String[] args) { reg(); login(); } public static void reg(){ doAction(6666); } public static void login(){ doAction(7777); } public static void doAction(int port){ //连接 Socket socket = null; BufferedReader br = null; BufferedWriter bw = null; try { //socket = new Socket("10.9.21.146", port); socket = new Socket("127.0.0.1", port); br = new BufferedReader(new InputStreamReader(socket.getInputStream())); bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); //处理数据 Scanner input = new Scanner(System.in); System.out.println("请输入用户名"); String username = input.next(); System.out.println("请输入密码"); String password = input.next(); //发送 bw.write(username + "#" + password); bw.newLine(); bw.flush(); String s = br.readLine(); System.out.println("服务器回复" + s); } catch (IOException e) { e.printStackTrace(); }finally { try { bw.close(); br.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * @Auther: xbh * @Date: 2019/8/13 10:06 * @Description: * Tcp服务器端,实现注册登录 * 实现步骤: * 注册线程 6666 * 登陆线程 7777 */ public class TcpServer { public static void main(String[] args) { System.out.println("服务器启动..."); Properties properties = new Properties(); //加载属性文件 File file = new File("userinfo.properties"); if (file.exists()){ try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } //加载 try { properties.load(new FileReader(file)); } catch (IOException e) { e.printStackTrace(); } new RegThread(properties).start(); new LoginThread(properties).start(); } }
第六节:UDP编程(了解)
-
概念
User Datagram Protocol的简称,用户数据包协议,提供面向事务的简单不可靠信息传送服务 特点: a.不安全 b.无连接 c.效率高 d.UDP传输数据时是有大小限制的,每个被传输的数据报必须限定在64KB之内
-
DatagramSocket: 数据报套接字,表示用来发送和接收数据报包的套接字。 DatagramPacket:此类表示数据报包。每个包最大64kb
-
单次发送数据
/** * @Auther: xbh * @Date: 2019/8/13 11:33 * @Description: */ public class Receiver { public static void main(String[] args) throws IOException { //创建DatagramSocket ,指定端口号 DatagramSocket ds = new DatagramSocket(9999); //创建接收数据报包 byte[] buf = new byte[1024*8]; DatagramPacket dp = new DatagramPacket(buf, buf.length); System.out.println("准备接收"); //接收,阻塞,没有数据阻塞,有数据接收返回 ds.receive(dp); //处理数据 String data = new String(dp.getData(), 0, dp.getLength()); System.out.println(dp.getAddress().getHostAddress() + "说" + data); //关闭 ds.close(); } } /** * @Auther: xbh * @Date: 2019/8/13 11:40 * @Description: */ public class Sender { public static void main(String[] args) throws IOException { //创建DatagramSocket,使用随机端口号 DatagramSocket ds = new DatagramSocket(); //创建发送数据包 String say = "好久不见"; DatagramPacket dp = new DatagramPacket(say.getBytes(), say.getBytes().length, InetAddress.getByName("10.9.21.146"), 9999); //发送 ds.send(dp); //关闭 ds.close(); } }
-
双向发送
/** * @Auther: xbh * @Date: 2019/8/13 11:33 * @Description: */ public class Receiver { public static void main(String[] args) throws IOException { //创建DatagramSocket ,指定端口号 DatagramSocket ds = new DatagramSocket(9999); //创建接收数据报包 byte[] buf = new byte[1024*8]; DatagramPacket dp = new DatagramPacket(buf, buf.length); System.out.println("准备接收"); //接收,阻塞,没有数据阻塞,有数据接收返回 ds.receive(dp); //处理数据 String data = new String(dp.getData(), 0, dp.getLength()); System.out.println(dp.getAddress().getHostAddress() + "说" + data); //回复 String say = "十分想念"; DatagramPacket dp2 = new DatagramPacket(say.getBytes(), say.getBytes().length, dp.getAddress(), dp.getPort()); ds.send(dp); //关闭 ds.close(); } } /** * @Auther: xbh * @Date: 2019/8/13 11:40 * @Description: */ public class Sender { public static void main(String[] args) throws IOException { //创建DatagramSocket,使用随机端口号 DatagramSocket ds = new DatagramSocket(); //创建发送数据包 String say = "老地方见"; DatagramPacket dp = new DatagramPacket(say.getBytes(), say.getBytes().length, InetAddress.getByName("10.9.21.146"), 9999); //发送 ds.send(dp); //接收回复 byte[] buf = new byte[1024*8]; DatagramPacket dp2 = new DatagramPacket(buf, buf.length); ds.receive(dp2); String data = new String(dp2.getData(), 0, dp2.getLength()); System.out.println(dp2.getAddress().getHostAddress() + "说" + data); //关闭 ds.close(); } }
-
UDP广播
/** * @Auther: xbh * @Date: 2019/8/13 12:03 * @Description: */ public class ChatReceiver { public static void main(String[] args) { new Thread(new Runnable(){ DatagramSocket ds = null; @Override public void run() { //创建DatagramSocket try { ds = new DatagramSocket(9999); byte[] buf = new byte[1024*20]; DatagramPacket dp = new DatagramPacket(buf, buf.length); while (true){ ds.receive(dp); String data = new String(dp.getData(), 0, dp.getLength()); if (data.equals("886")){ System.out.println(dp.getAddress().getHostAddress() + "退出了"); } } } catch (SocketException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { ds.close(); } } }).start(); new Thread(new Runnable(){ DatagramSocket ds = null; @Override public void run() { try { ds = new DatagramSocket(); Scanner input = new Scanner(System.in); while (true) { String data = input.next(); DatagramPacket dp = new DatagramPacket(data.getBytes(), data.getBytes().length, InetAddress.getByName("10.9.21.255"), 9999); ds.send(dp); if(data.equals("886")){ break; } } } catch (UnknownHostException e) { e.printStackTrace(); } catch (SocketException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { ds.close(); } } }).start(); } }
第二十二天 NIO
第一节 NIO简介
Java NIO 由以下几个核心部分组成: Buffer:缓冲区 Channel:通道 Selector:选择器(轮询器)
第二节 Buffer的使用
-
基本操作
/** * @Auther: xbh * @Date: 2019/8/13 14:49 * @Description: * 0 <= mark <= position <= limit <= capacity * private int mark = -1; 标记 * private int position 位置 * private int limit 限制 * private int capacity 容量 */ public class Demo2 { public static void main(String[] args) { ByteBuffer buffer = ByteBuffer.allocate(1024); System.out.println("写入数据之前= = = = = = = = = ="); System.out.println("容量" + buffer.capacity()); System.out.println("限制" + buffer.limit()); System.out.println("位置" + buffer.position()); buffer.put("helloworld".getBytes()); System.out.println("写入数据之后= = = = = = = = = ="); System.out.println("容量" + buffer.capacity()); System.out.println("限制" + buffer.limit()); System.out.println("位置" + buffer.position()); buffer.flip(); System.out.println("切换为读模式之后= = = = = = = = = ="); System.out.println("容量" + buffer.capacity()); System.out.println("限制" + buffer.limit()); System.out.println("位置" + buffer.position()); //读取 byte[] data = new byte[buffer.limit()]; buffer.get(data); System.out.println("读取数据之后= = = = = = = = = ="); System.out.println("容量" + buffer.capacity()); System.out.println("限制" + buffer.limit()); System.out.println("位置" + buffer.position()); buffer.rewind(); System.out.println("rewind,可以重复读= = = = = = = = = ="); System.out.println("容量" + buffer.capacity()); System.out.println("限制" + buffer.limit()); System.out.println("位置" + buffer.position()); byte b = buffer.get(); byte b1 = buffer.get(); byte b2 = buffer.get(); System.out.println("读取三个字节= = = = = = = = = ="); System.out.println("容量" + buffer.capacity()); System.out.println("限制" + buffer.limit()); System.out.println("位置" + buffer.position()); buffer.compact();//position指向没有读取的最大数,然后flip可以接着读 System.out.println("调用compact之后= = = = = = = = = ="); System.out.println("容量" + buffer.capacity()); System.out.println("限制" + buffer.limit()); System.out.println("位置" + buffer.position()); buffer.flip(); System.out.println("再次调用flip之后= = = = = = = = = ="); System.out.println("容量" + buffer.capacity()); System.out.println("限制" + buffer.limit()); System.out.println("位置" + buffer.position()); System.out.println("清空= = = = = = = = = ="); buffer.clear(); System.out.println("容量" + buffer.capacity()); System.out.println("限制" + buffer.limit()); System.out.println("位置" + buffer.position()); } }
-
buffer.mark()
public class Demo3 { public static void main(String[] args) { ByteBuffer buffer = ByteBuffer.allocate(1024); buffer.put("hello".getBytes()); buffer.flip(); byte[] data = new byte[2]; buffer.get(data); System.out.println("第一次读取"); System.out.println(new String(data)); buffer.mark();//制作标记,只能有一个标记 buffer.get(data); System.out.println("第二次读取"); System.out.println(new String(data)); buffer.reset();//回到上一个标记的位置 buffer.get(data); System.out.println("第三次读取"); System.out.println(new String(data)); } }
第三节 Channel
-
第一种方式
public class Demo4 { public static void main(String[] args) throws IOException { //创建RancdomAccessFile RandomAccessFile raf = new RandomAccessFile("C:\\Users\\xbh\\Desktop\\测试\\Task4.txt", "r"); //获取通道 FileChannel channel = raf.getChannel(); //读取 ByteBuffer buf = ByteBuffer.allocate(1024); while (channel.read(buf) > 0){ buf.flip();//切换为读取模式。装车的时候有写操作 String data = new String(buf.array(), 0, buf.limit(), "gbk");//buf.array(),缓冲区 System.out.println(data); buf.clear();//卸车 } //关闭 ,熄火 channel.close(); raf.close(); } }
-
第二种方式
public class Demo5 { public static void main(String[] args) throws IOException { FileChannel channel = FileChannel.open(Paths.get("C:\\Users\\xbh\\Desktop\\测试\\Task4.txt"), StandardOpenOption.READ); //读取 ByteBuffer buf = ByteBuffer.allocate(1024); while (channel.read(buf) > 0){ buf.flip();//切换为读取模式。装车的时候有写操作 String data = new String(buf.array(), 0, buf.limit(), "gbk");//buf.array(),缓冲区 System.out.println(data); buf.clear();//卸车 } //关闭 ,熄火 channel.close(); } }
-
循环读取
public class Demo5 { public static void main(String[] args) throws IOException { FileChannel channel = FileChannel.open(Paths.get("C:\\Users\\xbh\\Desktop\\测试\\Task4.txt"), StandardOpenOption.READ); //读取 ByteBuffer buf = ByteBuffer.allocate(1024); while (channel.read(buf) > 0){ buf.flip();//切换为读取模式。装车的时候有写操作 String data = new String(buf.array(), 0, buf.limit(), "gbk");//buf.array(),缓冲区 System.out.println(data); buf.clear();//卸车 } //关闭 ,熄火 channel.close(); } }
-
FileChannel加操作
public class Demo6 { public static void main(String[] args) throws IOException { //创建通道 FileChannel fileChannel = FileChannel.open(Paths.get("C:\\Users\\xbh\\Desktop\\测试\\Task4.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE,StandardOpenOption.APPEND);//按照顺序来执行 //写入 ByteBuffer buf = ByteBuffer.allocate(1024); for (int i = 0; i < 10; i++) { buf.put("好好学习".getBytes()); buf.flip(); fileChannel.write(buf); buf.clear(); } //关闭 fileChannel.close(); } }
-
复制文件
public class Demo7 { public static void main(String[] args) throws IOException { FileChannel readChannel = FileChannel.open(Paths.get("C:\\Users\\xbh\\Desktop\\测试\\Task5.PNG"), StandardOpenOption.READ); FileChannel writeChannel = FileChannel.open(Paths.get("C:\\Users\\xbh\\Desktop\\测试\\Task777.PNG"), StandardOpenOption.WRITE, StandardOpenOption.CREATE); ByteBuffer buf = ByteBuffer.allocate(1024); while (readChannel.read(buf) > 0){ buf.flip(); writeChannel.write(buf); buf.clear(); } readChannel.close(); writeChannel.close(); System.out.println("复制完毕"); } }
-
内存映射处理数据
/** * @Auther: xbh * @Date: 2019/8/13 16:31 * @Description: * 使用内存映射文件复制,在直接内存中开辟空间 */ public class Demo8 { public static void main(String[] args) throws IOException { FileChannel readChannel = FileChannel.open(Paths.get("C:\\Users\\xbh\\Desktop\\测试\\Task5.PNG"), StandardOpenOption.READ); FileChannel writeChannel = FileChannel.open(Paths.get("C:\\Users\\xbh\\Desktop\\测试\\Task777.PNG"), StandardOpenOption.WRITE, StandardOpenOption.CREATE); MappedByteBuffer map1 = readChannel.map(FileChannel.MapMode.READ_ONLY, 0, readChannel.size()); // MappedByteBuffer map1 = readChannel.map(FileChannel.MapMode.READ_ONLY, 0, 1024*1024*50); // MappedByteBuffer map2 = readChannel.map(FileChannel.MapMode.READ_ONLY, 1024*1024*50, 1024*1024*100); // MappedByteBuffer map3 = readChannel.map(FileChannel.MapMode.READ_ONLY, 1024*1024*100, readChannel.size()-(1024*1024*100)); writeChannel.write(map1); // writeChannel.write(map2); // writeChannel.write(map3); readChannel.close(); writeChannel.close(); System.out.println("复制完毕"); } }