栈:存储的是局部变量。使用完毕,立即回收。
堆:存储的是new出来的东西。实体,对象。有内存地址。
每一个对象都是有首地址的。每一个对象都是有默认值的。
使用完毕后,会被垃圾回收器在空闲的时候回收。
A: 在类中的位置不同
成员变量:类中,方法外
局部变量:方法中或者方法声明上(形式参数)
B: 在内存中的位置不同
成员变量:堆内存
局部变量:栈内存
C: 生命周期不同
成员变量:随着对象的创建而存在,随着对象的消失而消失
局部变量:随着方法的调用而存在,随着方法的调用完毕而消失
D: 初始化值的问题
成员变量:有默认值
局部变量:没有默认值。必须先定义,赋值,最后使用
Object:是类层次结构中的根类,所有的类都直接或者间接的继承自该类。
如果一个方法的形式参数是 Object,那么这里我们就可以传递它的任意的子类对象。
字符串的遍历:
A: 使用length ( ) 和charAt( ):char ch=string.charAt(i);
B: 把字符串转换为字符数组,然后遍历数组:char[] chs = s.toCharArray();
,再遍历chs数组
(1)自动生成构造方法:
代码区域右键 – Source – Generate Constructors from Superclass…
(2)无参构造方法
代码区域右键 – Source – Generate Constructor using Fields…
带参构造方法
(3)自动生成 getXxx()/setXxx():
代码区域右键 – Source – Generate Getters and Setters…
了解一些方法是怎么实现的,提高阅读代码的能力,参考学习标准代码的写法
在eclipse中,将鼠标放在想要查看的方法上,按住ctrl键,选择 “Open Implementation” 打开实现类,再选择对应包下的方法
前提是先将eclipse和源码路径关联
1、Thread
String getName() 返回该线程的名称。
void setName(String name) 改变线程名称,使之与参数 name 相同。
2、实现线程
(1) 继承 Thread类
通过 API 中搜索,查到 Thread 类。通过阅读 Thread 类中的描述。Thread 是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。
A:创建线程的步骤:
1.定义一个类继承 Thread。
2.重写 run 方法。
3.创建子类对象,就是创建线程对象。
4.调用 start 方法,开启线程并让线程执行,同时还会告诉 jvm 去调用 run 方法
(2)实现 Runnable 接口,该类实现 run 方法。
然后创建 Runnable 的子类对象,传入到某个线程的构造方法中,开启线程。
查看 Runnable 接口说明文档:Runnable 接口用来指定每个线程要执行的任务。包含了一个 run 的无参数抽象方法,需要由接口实现类重写该方法。
创建线程的步骤。
1、定义类实现 Runnable 接口。
2、覆盖接口中的 run 方法。。
3、创建 Thread 类的对象
4、将 Runnable 接口的子类对象作为参数传递给 Thread 类的构造函数。
5、调用 Thread 类的 start 方法开启线程。
3、多线程安全性问题出现的原因:
要有多个线程
要有被多个线程所共享的数据
多个线程并发的访问共享的数据
synchronized:同步(锁),可以修饰代码块和方法,被修饰的代码块和方法一旦被某个线程访问,则直接锁住,其他的线程将无法访问
同步:安全性高,效率低(有开锁关锁的步骤)
非同步:效率高,但是安全性低
(1)使用同步代码块解决
synchronized(锁对象){
//需要同步的代码
}
注意:锁对象需要被所有的线程所共享
(2)使用同步方法
格式:
修饰符 synchronized 返回值 方法名(){
}
同步方法:使用关键字 synchronized 修饰的方法,一旦被一个线程访问,则整个方法全部锁住,其他线程则无法访问
注意:
非静态同步方法的锁对象是 this
静态的同步方法的锁对象是当前类的字节码对象
package huan01;
/*
* 打印水仙花数
* 什么是水仙花数呢?
* 所谓的水仙花数是指一个三位数,其各位数字的立方和等于该数本身。
* 举例:153 就是一个水仙花数。
* 153 = 1*1*1 + 5*5*5 + 3*3*3
*
*
* 个位:153%10 = 3;
* 十位:153/10%10 = 5;
* 百位:153/10/10%10 = 1;
* 千位:...
* 万位:...
*
* */
public class ForSum01 {
public static void main(String[] args) {
int count=0;
for (int i = 100; i < 1000; i++) {
int a=i % 10;
int b=i/10 %10;
int c=i/10/10%10;
if ( (a*a*a + b*b*b + c*c*c )== i) {
System.out.println(i+"这个数是水仙花数");
count++;
}
}
System.out.println("水仙花总数为:"+count);
}
}
我在写这个类的时候,偷懒直接给类名起为Random.java,然后在代码int number = r.nextInt(100)+1;
报错说The method nextInt(int) is undefined for the type RandomTest
,查了查之后,发现是,类名最好不要和API类名相同,如果要使用,要使用全路径,否则会把Java提供的类给覆盖掉,而自己又没有重写nextInt()方法。
package huan01;
import java.util.*;
// 系统产生一个 1-100 之间的随机数,请猜出这个数据是多少
public class RandomTest {
public static void main(String[] args) {
// 系统产生一个随机数 1-100 之间的。
Random r=new Random();
int number = r.nextInt(100)+1; //获取随机数[1,100]
while(true) { //猜中就结束
System.out.println("请输入你要猜的数字:");
Scanner scanner=new Scanner(System.in);
int guessNumber=scanner.nextInt();
if (guessNumber > number) {
System.out.println("你输入的"+guessNumber+"大了");
}
if (guessNumber < number) {
System.out.println("你输入的"+guessNumber+"小了");
}
if (guessNumber == number) {
System.out.println("猜中!");
break;
}
System.out.println();
}
}
}
package huan01;
import java.util.concurrent.CountDownLatch;
/*
* 需求:打印 5 位数中的所有回文数。
* 什么是回文数呢?举例:12321 是回文数,个位与万位相同,十位与千位相同。
*
* */
public class HuiWenShu {
public static void main(String[] args) {
int count=0;
for (int i = 10000; i < 100000; i++) {
int ge=i%10;
int shi=i/10%10;
int qian=i/10/10/10%10;
int wan=i/10/10/10/10%10;
if ((ge == wan) && (shi == qian)) {
System.out.println(i+"是回文数");
count++;
}
}
System.out.println("共有"+count+"个五位数的回文数");
}
}
**需求:**有一对兔子,从出生后第 3 个月起每个月都生一对兔子,小兔子长到第三个月后每个月又
生一对兔子,假如兔子都不死,问第二十个月的兔子对数为多少?
**规律:**从第三个月开始,每个月的兔子对数是前两个月的兔子对数之和。
第一个月和第二个月的兔子对数是 1
package huan01;
/*
* 需求:
* 有一对兔子,从出生后第 3 个月起每个月都生一对兔子,小兔子长到第三个月后每个月又
生一对兔子,假如兔子都不死,问第二十个月的兔子对数为多少?
* */
/*规律:从第三个月开始,每个月的兔子对数是前两个月的兔子对数之和。
第一个月和第二个月的兔子对数是 1
* */
public class Rabbit {
public static void main(String[] args) {
int[] a=new int[20];
a[0]=1;
a[1]=1;
for (int i = 2; i < a.length; i++) {
a[i]=a[i-1]+ a[i-2];
}
System.out.println("第二十个月兔子的数目为:"+a[19]);
}
}
package huan01;
import java.util.Scanner;
public class ReverseArray {
public static void main(String[] args) {
int[] a=new int[5];
input(a);
reverse(a);
print(a);
}
//输入
public static void input(int[] a) {
System.out.println("输入长度为5的数组");
Scanner sc=new Scanner(System.in);
for (int i = 0; i < a.length; i++) {
System.out.println("输入第"+(i+1)+"个数:");
a[i]=sc.nextInt();
}
}
//反转数组
public static void reverse(int[] a) {
for (int start = 0,end=a.length-1; start <= end; start++,end--) {
int temp=a[start];
a[start]=a[end];
a[end]=temp;
}
}
//打印数组
public static void print(int[] a) {
for (int i = 0; i < a.length; i++) {
System.out.println("数组第"+(i+1)+"位为:"+a[i]);
}
}
}
输入密码和账号,验证是否和存储的密码账号一致,当输入两次以后,锁定账号
package huan01;
import java.util.Scanner;
/*模拟登录,给三次机会,并提示还有几次。
* */
public class UserLoad {
public static void main(String[] args) {
// 已经存在的用户名和密码
String username = "admin";
String password = "admin";
//键盘录入用户名和密码
int i=2;
while(i>0) {
Scanner sc= new Scanner(System.in);
System.out.println("请输入用户名");
String name=sc.nextLine();
System.out.println("请输入密码:");
String pwd=sc.nextLine();
if (name.equals(username) && password.equals(pwd)) {
System.out.println("输入正确");
break;
}
else {
System.out.println("密码或账号输入错误");
}
i--;
}
if(i == 0) {
System.out.println("您的账号已锁定,请联系管理员!");
}
}
}
键盘录入一个字符串,把该字符串的首字母转成大写,其余为小写。(只考虑英文大小写字母字符)
package huan01;
import java.util.Scanner;
/* 键盘录入一个字符串,把该字符串的首字母转成大写,
* 其余为小写。(只考虑英文大小写字母字符)
* */
public class StringExchange {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入字符串:");
String string=sc.nextLine();
String s1=string.substring(0,1); //第一个字母
String s2=string.substring(1,string.length()); //首字母剩下的字符串
//String s2=string.substring(1);
String s3=s1.toUpperCase()+s2.toLowerCase();
System.out.println("转换后的字符串为"+s3);
}
}
package huan01;
import java.util.Scanner;
/**
* @author huan
* @version 创建时间:2020年3月11日 下午9:56:45
*
*/
public class StringBuilderTest {
public static void main(String[] args) {
System.out.println("选择1是StringBuilder 和 String 的相互转换");
System.out.println("选择2是 把数组按照格式拼接成一个字符串");
System.out.println("选择3是把字符串反转 ");
System.out.println("选择4是判断字符串是否是对称的");
System.out.println("请选择:");
Scanner sc = new Scanner(System.in);
int i = sc.nextInt();
switch (i) {
case 1:
BuildtoString();
break;
case 2:
System.out.println("转换的数组为arr[]={1,2,3}");
int[] arr={1,2,3};
arrayToString(arr);
break;
case 3:
System.out.println("反转的字符串为abcdefg");
myReverse("abcdefg");
break;
case 4:
System.out.println("判断的字符串为:ababa");
isSymmetry("ababa");
default:
break;
}
}
/*
* StringBuilder 和 String 的相互转换
*
* StringBuilder -- String
* public String toString():通过 toString()就可以实现把 StringBuilder 转成 String
*
* String -- StringBuilder
* StringBuilder(String str): 通 过 构 造 方 法 就 可 以 实 现 把 String 转 成StringBuilder
*
* */
public static void BuildtoString() {
System.out.println("StringBuilder 和 String 的相互转换");
StringBuilder sb1= new StringBuilder();
sb1.append("hello").append(" world");
String s1=sb1.toString();
System.out.println("由StringBuilder转换的字符串:"+s1);
String s2= "hello world";
StringBuilder sb2= new StringBuilder(s2);
System.out.println("由String转换的StringBuilder:"+sb2);
System.out.println();
}
/*
* 把数组拼接成一个字符串,直接用字符串拼接会造成内存浪费,还耗时
* 举例:
* int[] arr = {1,2,3};
* 结果:
* [1, 2, 3]
*/
public static void arrayToString(int[] arr) {
StringBuilder sb=new StringBuilder();
sb.append("[");
for (int i = 0; i < arr.length; i++) {
if (i == arr.length-1 ) {
sb.append(arr[i]);
}else {
sb.append(arr[i]).append(", ");
}
}
sb.append("]");
System.out.println(sb.toString());
}
/*
* 把字符串反转
*
* 分析:
* A:键盘录入一个字符串
* B:写方法实现反转
* String -- StringBuilder -- reverse() -- String
* C:调用方法
* D:输出结果
*/
public static void myReverse(String s) {
//String -- StringBuilder -- reverse() -- String
StringBuilder sb = new StringBuilder(s);
sb.reverse();
System.out.println(sb.toString());
}
/*
* 判断一个字符串是否是对称字符串
* 例如"abc"不是对称字符串,"aba"、"abba"、"aaa"、"mnanm"是对称字符串
*
* 分析:
* A:键盘录入一个字符串
* B:写方法实现判断一个字符串是否是对称字符串
* 把字符串反转,和反转前的字符串进行比较,如果内容相同,就说明是对称字符串
* C:调用方法
* D:输出结果
*/
public static void isSymmetry(String s) {
//把字符串反转,和反转前的字符串进行比较,如果内容相同,就说明是对称字符串
StringBuilder sb = new StringBuilder(s);
sb.reverse();
String result = sb.toString();
if (result.equals(s)) {
System.out.println("该字符串是对称字符串");
}
else {
System.out.println("该字符串不是对称字符串");
}
}
}
A: 利用基本流一次读写一个字符
B: 利用基本流一次读写一个字符数组
C: 利用缓冲流一次读写一个字符
D: 利用缓冲流一次读写一个字符数组
E: 利用缓冲流的特有方法一次读写一个字符串,推荐!
package huan2;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/**
* @author huan
* @version 创建时间:2020年3月13日 下午6:24:26
*
*/
/*
* 复制文本文件(5 种方式)
*
* 数据源:
* ForSum01.java
* 目的地:
* Copy.java
*/
public class FileCopy {
public static void main(String[] args) throws IOException {
String srcFileName = "ForSum01.java";
String destFileName = "Copy.java";
method1(srcFileName,destFileName);
// method2(srcFileName,destFileName);
//method3(srcFileName,destFileName);
// method4(srcFileName,destFileName);
// method5(srcFileName,destFileName);
}
//缓冲流一次读写一个字符串,推荐这种写法
public static void method5(String srcFileName,String destFileName)throws IOException {
//创建输入缓冲流对象
BufferedReader br = new BufferedReader(new FileReader(srcFileName));
//创建输出缓冲流对象
BufferedWriter bw = new BufferedWriter(new
FileWriter(destFileName));
//一次读写一个字符串
String line;
while((line=br.readLine())!=null){
bw.write(line);
bw.newLine();
bw.flush();
}
//释放资源
bw.close();
br.close();
}
//缓冲流一次读写一个字符数组
public static void method4(String srcFileName,String destFileName) throws IOException {
//创建输入缓冲流对象
BufferedReader br = new BufferedReader(new FileReader(srcFileName));
//创建输出缓冲流对象
BufferedWriter bw = new BufferedWriter(new
FileWriter(destFileName));
//一次读写一个字符数组
char[] chs = new char[1024];
int len;
while((len=br.read(chs))!=-1) {
bw.write(chs,0,len);
}
//释放资源
bw.close();
br.close();
}
//缓冲流一次读写一个字符
public static void method3(String srcFileName,String destFileName) throws IOException {
//创建输入缓冲流对象
BufferedReader br = new BufferedReader(new FileReader(srcFileName));
//创建输出缓冲流对象
BufferedWriter bw = new BufferedWriter(new
FileWriter(destFileName));
//一次读写一个字符
int ch;
while((ch=br.read())!=-1) {
bw.write(ch);
}
//释放资源
bw.close();
br.close();
}
//基本流一次读写一个字符数组
public static void method2(String srcFileName,String destFileName)throws IOException {
//创建输入流对象
FileReader fr = new FileReader(srcFileName);
//创建输出流对象
FileWriter fw = new FileWriter(destFileName);
//一次读写一个字符数组
char[] chs = new char[1024];
int len;
while((len=fr.read(chs))!=-1) {
fw.write(chs,0,len);
}
//释放资源
fw.close();
fr.close();
}
//基本流一次读写一个字符
public static void method1(String srcFileName,String destFileName) throws IOException {
//创建输入流对象
FileReader fr = new FileReader(srcFileName);
//创建输出流对象
FileWriter fw = new FileWriter(destFileName);
//一次读写一个字符
int ch;
while((ch=fr.read())!=-1) {
fw.write(ch);
}
//释放资源
fw.close();
fr.close();
}
}
package huan2;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
/**
* @author huan
* @version 创建时间:2020年3月13日 下午6:36:13
*
*/
/*
* 把 ArrayList 集合中的字符串数据存储到文本文件
* 每一个字符串元素作为文件中的一行数据
*/
/*
* 从文本文件中读取数据到 ArrayList 集合中,并遍历集合
* 每一行数据作为一个字符串元素
*/
public class FileTest03 {
public static void main(String[] args) throws IOException {
ArrayList<String> array= new ArrayList<String>();
array.add("hello");
array.add("world");
array.add("java");
ArrayListToFile(array, "array.txt");
FileToArrayList("array.txt");
}
//把 ArrayList 集合中的字符串数据存储到文本文件
public static void ArrayListToFile(ArrayList<String> array,String file) throws IOException {
BufferedWriter bw= new BufferedWriter(new FileWriter(file));
for (int i = 0; i < array.size(); i++) {
String s= array.get(i);
bw.write(s);
bw.newLine();
bw.flush();
}
bw.close();
}
//把文本文件中的数据读取到集合
public static void FileToArrayList(String file) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(file));
ArrayList<String> array=new ArrayList<String>();
String line;
while((line = br.readLine())!=null) {
array.add(line);
}
br.close();
for (int i = 0; i < array.size(); i++) {
String s= array.get(i);
System.out.println(s);
}
}
}
public class CalendarDemo {
public static void main(String[] args) {
Calendar c = Calendar.getInstance();
//void set(int field, int value):把指定的字段修改成指定的值
c.set(Calendar.DAY_OF_MONTH,20); //修改
//void add(int field, int amount):在指定的字段上加上指定的值
c.add(Calendar.DAY_OF_MONTH, 1);
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH)+1; //是从0到11
int day = c.get(Calendar.DAY_OF_MONTH);
System.out.println(year+ "年"+ month + "月" + day + "日");
}
}
需求:判断一个数是否符合 int 类型的范围
由于基本数据类型只能做一些简单的操作和运算,所以 Java 为我们封装了基本数据类型,为每
种基本数据类型提供了包装类
包装类就是封装了基本数据类型的类,为我们提供了更多复杂的方法和一些变量
其中需要注意 int 对应的是 Integer,char 对应的 Character,其他 6 个都是基本类型
自动拆箱:对象转成基本数值
自动装箱:基本数值转成对象
public class IntegerDemo {
public static void main(String[] args) {
Integer i = new Integer("2147483647"); //超出int 范围就会报错
System.out.println(i);
int a = i.intValue(); //String 转换成 int 类型
System.out.println(a+10);
int b = Integer.parseInt("20"); //String 转换成 int 类型
System.out.println(b+30);
Integer i2 = new Integer(40);
String s = i2.toString(); /*int 转换成 String 类型,或者直接+""更简单*/
System.out.println(s);
String s2 = Integer.toString(50);
System.out.println(s2);
}
}
需求:
校验qq号码
要求必须5-15位
0不能开头
必须都是数字
正则表达式:一套规则,可以用来匹配字符串
常用的正规表达式字符:
字符
x 字符 x
\\ 反斜线字符
字符类
[abc] a、b 或 c(简单类)
[^abc] 任何字符,除了 a、b 或 c(否定)
[a-zA-Z] a 到 z 或 A 到 Z,两头的字母包括在内(范围
预定义字符类
. 任何字符(与行结束符可能匹配也可能不匹配)
\d 数字:[0-9]
\D 非数字: [^0-9]
\s 空白字符:[ \t\n\x0B\f\r]
\S 非空白字符:[^\s]
\w 单词字符:[a-zA-Z_0-9]
\W 非单词字符:[^\w]
Greedy 数量词
X? X,一次或一次也没有
X* X,零次或多次
X+ X,一次或多次
X{n} X,恰好 n 次
X{n,} X,至少 n 次
X{n,m} X,至少 n 次,但是不超过 m 次
实现代码:
public class RegexDemo {
public static void main(String[] args) {
String qq = "12345";
/*
boolean flag= checkQQ(qq);
System.out.println(flag);*/
boolean flag = qq.matches("[1-9][0-9]{4,14}");
System.out.println(flag);
}
//自己写的
public static boolean checkQQ(String qq) {
int length = qq.length();
//要求必须是 5-15 位
if(length < 5 || length > 15) {
return false;
}
//0 不能开头
if(qq.startsWith("0")) {
return false;
}
//必须都是数字
for (int i = 0; i < length; i++) {
//得到参数的每一个字符
char c = qq.charAt(i);
if(c < '0' || c > '9') {
return false;
}
}
return true;//符合要求
}
}
集合的遍历方式:
有些集合没有索引,可以使用迭代器遍历
public class IteratorDemo {
public static void main(String[] args) {
//创建集合对象
Collection c = new ArrayList(); //java多态,父类引用指向子类
c.add("hello");
c.add("java");
c.add("hhh");
//获取迭代器对象
Iterator it = c.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
//method();
}
// 通过索引遍历
private static void method() {
//创建集合对象
Collection c = new ArrayList();
//添加元素
c.add("hello");
c.add("world");
c.add("java");
//获取数组
Object[] objs = c.toArray();
//遍历数组
for (int i = 0; i < objs.length; i++) {
System.out.println(objs[i]);
}
}
}
注意,若使用迭代器遍历,修改(添加)集合时,可能会出现并发修改异常。
Exception in thread “main” java.util.ConcurrentModificationException:并发修改异常
这是因为迭代器是依赖于集合的,相当于集合的一个副本,当迭代器在操作的时候,如果发现和集合不一样,则抛出异常
并发修改异常解决方案:
A:不使用迭代器遍历集合,就可以在遍历的时候使用集合的方法进行增加或删除
B:依然使用迭代器遍历,那么就需要使用 Iterator 的子接口 ListIterator 来实现向集合中添加
需求:判断集合中是否包含元素 java,如果有则添加元素 android(对集合有增加)
修改后的代码:
public static void main(String[] args) {
List c = new ArrayList();
c.add("hello");
c.add("java");
c.add("hhh");
ListIterator lit = c.listIterator(); //是Iterator的子类
while(lit.hasNext()) {
String s = (String)lit.next();
if(s.equals("java")) {
lit.add("aaaa"); //不能使用集合的添加,要使用迭代器的添加
}
}
System.out.println(c);
//method2();
//method();
}
具体规则:
1. 组装 54 张扑克牌
2. 将 54 张牌顺序打乱
3. 三个玩家参与游戏,三人交替摸牌,每人 17 张牌,最后三张留作底牌。
4. 查看三人各自手中的牌、底牌
/*
* 模拟斗地主发牌
组装扑克牌
洗牌
发牌
*/
public class CollectionsTest {
public static void main(String[] args) {
// 组装扑克牌
String[] arr = {"黑桃","红桃","方片","梅花"};
String[] arr2= {"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
//牌盒
ArrayList<String> box = new ArrayList<String>();
//添加每张牌
for(int i = 0; i<arr.length; i++) {
for(int j=0; j<arr2.length; j++) {
//获取每张牌
box.add(arr[i]+arr2[j]);
}
}
box.add("大王");
box.add("小王");
/*System.out.println(box);
System.out.println(box.size());*/
//洗牌,随机打乱
Collections.shuffle(box);
//三个人斗地主
ArrayList<String> aa = new ArrayList<String>();
ArrayList<String> bb = new ArrayList<String>();
ArrayList<String> cc = new ArrayList<String>();
// 发牌, 留三张牌作为底牌
for(int i=0; i<box.size()-3; i++) {
if( i%3 == 0) {
aa.add(box.get(i));
}
if( i%3 ==1) {
bb.add(box.get(i));
}
if( i%3 == 2) {
cc.add(box.get(i));
}
}
//
System.out.println("aa:"+aa);
System.out.println("bb:"+bb);
System.out.println("cc:"+cc);
//底牌
for(int i= box.size()-3; i <box.size(); i++) {
System.out.println(box.get(i));
}
}
}
学生类 Student:String name; int age
public class HashMapDemo2 {
public static void main(String[] args) {
//若需要使Student类不能重复,需要重写Student的hashCode和equals方法
//创建Map对象
Map<Student, String> hm = new HashMap<Student, String>();
//创建key对象
Student s1 = new Student("aaa", 18); // name, age
Student s2 = new Student("bbb", 20);
Student s3 = new Student("ccc", 25);
//添加映射关系
hm.put(s1,"001");
hm.put(s2, "002");
hm.put(s3, "003");
//遍历Map对象
//方式1:获取所有的key,通过key来获取value
Set<Student> keys = hm.keySet(); //因为key不可重复,所有返回的是Set
for(Student key:keys) {
String value = hm.get(key);
System.out.println(key+"=" + value);
}
System.out.println("-----------");
//方式2:获取所有对象,通过对象获取key和value
Set<Map.Entry<Student, String>> entrys = hm.entrySet();
for(Entry<Student, String> entry: entrys) {
Student key = entry.getKey();
String value = entry.getValue();
System.out.println(key+"="+value);
}
}
}
古典问题:有一对兔子,从出生后第 3 个月起每个月都生一对兔子,
小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,
问第二十个月的兔子对数为多少?
规律:除了第一个月和第二月以外,其余每个月都是前两个月之和
斐波那契列数
//使用迭代
public class Rabbit {
public static void main(String[] args) {
int num = rabbit(20);
System.out.println(num); //6765
}
public static int rabbit(int a) {
if( a ==1 || a==2) {
return 1;
}else {
return rabbit(a-2)+rabbit(a-1);
}
}
}
需求:输出指定目录下所有的 java 文件名(包含子目录)
public class RecurrenceTest {
public static void main(String[] args) {
File file = new File("D:\\myRecurrence"); //传入需要删除Java文件的目录
deleteJava(file);
}
public static void deleteJava(File file) {
File[] f = file.listFiles();
for (File file2 : f) {
if(file2.isFile()) { // 是文件
if(file2.getName().endsWith("java")) { //以java结尾,getName()返回的是字符串
System.out.println("删除的文件名为:"+file2.getName());
file2.delete(); //要注意,该方法不走回收站,谨慎!
}
}else { //是目录,只可能是目录或文件,或再用isDirectory()判断一下也可
deleteJava(file2); //递归
}
}
}
}
/*
* 需求:输出指定目录下所有的 java 文件名(包含子目录)
*/
public class RecurrenceTest01 {
public static void main(String[] args) {
File file = new File("src");
findJava(file);
}
public static void findJava(File file) {
File[] f = file.listFiles();
for (File file2 : f) {
if(file2.isFile()) {
System.out.println(file2.getName());
}else { //是目录
findJava(file2);
}
}
}
}
字节流可以操作所有类型的文件,因为所有的文件在计算机中都是以字节形式存储
而字符流只能用来操作纯文本类型的文件,不能操作字节类型的文件
二进制文件(比如视频、音频)只能使用字节流进行复制(使用 windows 自带记事本打开读不懂的文件)
文本文件的复制既可使用字符流,也可以使用字节流
// 使用字节流复制图片
//若使用字符流,虽然能够复制,但是复制后的文件无法正常读取
public class CopyImage {
public static void main(String[] args) throws IOException {
//创建字节输入流对象
FileInputStream fis = new FileInputStream("D:\\5 Study\\00 笔记\\08aa.jpg"); //文件需要存在,且路径需要有两个斜杠
//创建字节输出流对象
FileOutputStream fos = new FileOutputStream("D:\\08aa.jpg");
//一次读写一个字节数组
int len; //用于存储读到的字节个数
byte[] bys = new byte[1024]; //数组长度一般为1024的整数倍
while( (len = fis.read(bys)) != -1) { //读到多少个,就写入多少个
fos.write(bys,0,len);
}
// 释放资源
fos.close();
fis.close();
}
}
需求:读取项目根目录下的 aa.java,并输出到命令行
数据源:项目根目录下的 aa.java BufferedReader
目的地:命令行 System.out
由于标准输出流是一个字节输出流,所以只能输出字节或者字节数组,但是我们读取到的数据则是字符串,如果想进行输出还需要转换成字节数组
我们要想通过标准输出流输出字符串,把标准输出流转换成一种字符输出流即可,OutputStreamWriter
public class OutputStreamWriterDemo {
public static void main(String[] args) throws IOException {
//创建输入流对象
BufferedReader br = new BufferedReader(new FileReader("aa.java"));
//创建输出流对象
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
//进行数据的读写
String line; //用于存储读取到的数据
while((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
}
//释放资源
bw.close();
br.close();
}
}
需求:读取键盘录入的数据,并输出到项目根目录下的 a.txt 文件中
数据源:读取键盘录入的数据 System.in(标准输入流)
目的地:项目根目录下的 a.txt FileWriter(字符输出流)
转换流:需要把字节输入流转换成字符输入流,InputStreamReader
InputStreamReader(InputStream in) 转换流
//在控制台输入数据后,refresh项目文件即可看到内容
public class InputStreamReaderDemo {
public static void main(String[] args) throws IOException {
// 创建输入流对象
InputStream is = System.in;
Reader r = new InputStreamReader(is);
//创建输出流对象
FileWriter fw = new FileWriter("a.txt");
//读写数据
char[] chs = new char[1024];
int len;
while((len = r.read(chs)) != -1) {
fw.write(chs,0,len);
fw.flush(); //刷新缓冲区
}
fw.close();
is.close();
}
}
利 用 打 印 流 将 根 目 录 下 的 aa.java 复 制 到 d:\aa.java 下
/*
* 使用打印流复制文本文件
*
* 数据源aa.java BufferedReader
* 目的地d:\\aa.java PrintWriter
*
*/
public class PrintWriterDemo3 {
public static void main(String[] args) throws IOException {
//创建输入流对象
BufferedReader br = new BufferedReader(new FileReader("aa.java"));
//创建打印流对象
PrintWriter pw = new PrintWriter(new FileWriter("D:\\aa.java"),true); //开启自动刷新
String line; //用于存储读取到的每行数据
while((line = br.readLine()) != null) {
pw.println(line); //自动换行
}
pw.close();
br.close();
}
}
1、对象操作流:可以用于读写任意类型的对象
注意:
2、利用序列化流读取对象
(1)使用对象输出流和读对象输入流写对象
异常:Exception in thread “main” java.io.NotSerializableException:
com.itheima_07.Student
(2)Serializable:序列号,是一个标识接口,只起标识作用,没有方法
当一个类的对象需要 IO 流进行读写的时候,这个类必须实现该接口
(3)异常:Exception in thread “main” java.io.EOFException:当输入过程中意外到达文件或流的末尾时,抛出此异常。
public class Student implements Serializable{ //实现序列化接口
/* 点击黄色警告线后,选择 add generated serial version ID 后自动生成序列号*/
private static final long serialVersionUID = 6361890890437825953L;
String name;
int age;
String gender;
public Student (String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", gender=" + gender + "]";
}
}
(4)解决对象输入流读取对象出现异常的问题:因为不明确需要读取多少次,读到文件末尾时会抛出异常,因此为明确需要读取的次数,使用集合,解决类的黄色警告问题
(5)解决读写版本不一致问题、
对象和类都有序列号,当类的序列号变化了,但对象的序列号没有发生变化,就会出错
(6)异常: Exception in thread “main” java.io.InvalidClassException
当 Serialization 运行时检测到某个类具有以下问题之一时,抛出此异常。
该类的序列版本号与从流中读取的类描述符的版本号不匹配
该类包含未知数据类型
该类没有可访问的无参数构造方法
因此将学生类的序列号固定。
public class ObjectOutputStreamDemo5 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//method(); //要先写入才能读取,否则会报错
method2();
}
//读取学生对象
private static void method2() throws IOException, FileNotFoundException, ClassNotFoundException {
//创建对象输入流的对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
//读取对象
Object obj = ois.readObject();
//向下转型,获取具体的子类对象
ArrayList<Student> list = (ArrayList<Student>) obj;
for (Student student : list) {
System.out.println(student);
}
//释放资源
ois.close();
}
//写出学生对象
private static void method() throws IOException, FileNotFoundException {
//创建对象输出流的对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
//创建集合对象
ArrayList<Student> list = new ArrayList<Student>();
//添加学生对象
list.add(new Student("ssss", 30));
list.add(new Student("aaa", 31));
//写出学生对象
oos.writeObject(list);
//释放资源
oos.close();
}
}
需求:用三个线程模拟三个售票窗口,共同卖 100 张火车票,每个线程打印出卖的第几张票
package com.itheima_03;
/**
* @author huan
* @version 创建时间:2020年4月26日 上午11:15:26
*
*/
public class TicketThread implements Runnable{
static int tickets = 100;
Object obj = new Object();
@Override
public void run() {
//出售火车票
while(true) {
method(); //在同步代码块中调用
}
}
private static synchronized void method() { //同步方法
if(tickets > 0 ) { //成员变量也需要用static
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//链式编程
System.out.println(Thread.currentThread().getName()+":"+tickets--);
}
}
}
package com.itheima_04;
import com.itheima_03.TicketThread;
/**
* @author huan
* @version 创建时间:2020年4月26日 上午11:16:30
*
*/
//需求:用三个线程模拟三个售票窗口,共同卖 100 张火车票,每个线程打印出卖第几张票
public class TicketTest {
public static void main(String[] args) {
//创建线程对象
TicketThread tt = new TicketThread();
Thread t1 = new Thread(tt);
t1.setName("窗口1");
Thread t2 = new Thread(tt);
t2.setName("窗口2");
Thread t3 = new Thread(tt);
t3.setName("窗口3");
//启动线程对象
t1.start();
t2.start();
t3.start();
}
}
(1)使用 UDP 协议发送数据
创建发送端 Socket 对象
创建数据并打包
发送数据
释放资源
public class SendDemo {
public static void main(String[] args) throws IOException {
//创建发送端Socket对象
DatagramSocket ds = new DatagramSocket();
//创建数据并打包
/*
* DatagramPacket :此类表示数据报包
* 数据 byte[]
* 设备的地址 ip
* 进程的地址 端口号
DatagramPacket(byte[] buf, int length, InetAddress address, int port)
*/
String s = "send message:hello!";
byte[] bys = s.getBytes();
int length = bys.length;
InetAddress address = InetAddress.getByName("huan"); //发送给当前设备
int port = 8888; //端口不能已经被绑定
//打包
DatagramPacket dp = new DatagramPacket(bys, length,address,port);
//发送数据
ds.send(dp);
//释放资源
ds.close();
}
}
(2)使用 UDP 协议接收数据
创建接收端 Socket 对象
接收数据
解析数据
输出数据
释放资源
public class ReceiveDemo {
public static void main(String[] args) throws IOException {
//创建接收端Socket对象
DatagramSocket ds = new DatagramSocket(8888); //需要和发送端的端口一样
//接收数据
// DatagramPacket(byte[] buf, int length)
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys, bys.length);
//System.out.println("");
ds.receive(dp); //在这被阻塞
//System.out.println("");
//解析数据
// InetAddress getAddress() : 获取发送端的 IP 对象
InetAddress address = dp.getAddress();
//byte[] getData() :获取接收到的数据,也可以直接使用创建包对象时的数组
byte[] data = dp.getData();
//int getLength() :获取具体收到数据的长度
int length = dp.getLength();
//输出数据
System.out.println("sender -- >" +address.getHostAddress());
System.out.println(new String(bys,0,length));
//释放资源
ds.close();
}
}
(3)需要运行发送端,接收端才可以收到数据。在控制台查看接收端的数据。
客户端代码:
package com.itheima_05;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* @author huan
* @version 创建时间:2020年4月26日 下午10:04:56
*
*/
/*
需求:使用 TCP 协议发送数据,并将接收到的数据转换成大写返回
客户端发出数据
服务端接收数据
服务端转换数据
服务端发出数据
客户端接收数据
*/
public class ClientDemo {
public static void main(String[] args) throws IOException{
// 创建客户端Socket对象
Socket s = new Socket(InetAddress.getByName("huan"),10010);
// 获取输出流对象
OutputStream os = s.getOutputStream();
//发出数据
os.write("tcp:im somming again!".getBytes());
//获取输出流对象
InputStream is = s.getInputStream();
byte[] bys = new byte[1024];
int len; //用于存储读取到的字节个数
//接收数据
len=is.read(bys);
//输出数据
System.out.println(new String(bys,0,len));
//释放资源
s.close();
}
}
服务端代码:
package com.itheima_05;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author huan
* @version 创建时间:2020年4月26日 下午10:12:43
*
*/
public class ServerDemo {
public static void main(String[] args) throws IOException {
//创建服务端Socket对象
ServerSocket ss = new ServerSocket(10010);
//监听
Socket s = ss.accept();
//获取输入流对象
InputStream is = s.getInputStream();
//获取数据
byte[] bys = new byte[1024];
int len; //用于存储读取到的字节个数
len = is.read(bys);
String str = new String(bys,0,len);
//输出数据
System.out.println("读取到的数据为:"+str);
//转换数据,转为大写
String upperStr = str.toUpperCase();
//获取输出流对象
OutputStream os = s.getOutputStream();
//返回数据(发出数据)
os.write(upperStr.getBytes());
//释放资源
s.close();
//ss.close(); //服务器一般不关闭
}
}
因为TCP协议需要双方确认连接,所以需要先启动服务端,否则会报错。
(1)将用户名和密码封装到一个 User 类中,提供对应的构造方法和 getter/setter 方法。
新建一个 UserDB 类里面定义一个集合,在集合中添加以下 User 对象
new User(“zhangsan”,“123456”);
new User(“lisi”,“654321”);
new User(“itheima”,“itheima”);
new User(“admin”,“password”);
(2)客户端:
1.提示用户输入用户名和密码,将用户输入的用户名和密码发送给服务端
2.接收服务端验证完用户名和密码的结果
(3)服务端:
1.服务端将客户端发送过来的用户名密码封装成 User 对象
2.集合中如果包括这个 User 对象,想客户端写入” 登录成功”,否则向客户端写入”登录失败”
用户类:
package com.itheima_07;
/**
* @author huan
* @version 创建时间:2020年4月26日 下午10:25:41
*
*/
public class User {
private String username; //用户名
private String password; //密码
public User() {
super();
// TODO Auto-generated constructor stub
}
public User(String username, String password) {
super();
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
模拟数据库的类,初始化集合,存储用户名和密码:
package com.itheima_07;
import java.util.ArrayList;
import java.util.List;
/**
* @author huan
* @version 创建时间:2020年4月26日 下午10:25:55
*
*/
public class UserDB { //模拟数据库
private static List<User> users = new ArrayList<User>();
//进行初始化
static {
users.add(new User("aa","123"));
users.add(new User("bb","456"));
users.add(new User("cc","789"));
users.add(new User("admin","password"));
}
//公共的方法
public static List<User> getUser(){
return users;
}
}
客户端:
package com.itheima_07;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
/**
* @author huan
* @version 创建时间:2020年4月26日 下午10:25:14
*
*/
//模拟用户登录
public class ClientTest {
public static void main(String[] args) throws IOException {
//创建客户端Socket对象
Socket s = new Socket("huan",8888); //主机名和端口号
//获取用户名和密码,通过标准输入流
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入用户名:");
String username = br.readLine();
System.out.println("请输入密码:");
String password = br.readLine();
//获取输入流对象
PrintWriter out = new PrintWriter(s.getOutputStream(),true); //自动换行
//写出数据
out.println(username);
out.println(password);
//获取输入流对象
BufferedReader serberBr = new BufferedReader(new InputStreamReader(s.getInputStream()));
//获取服务器返回的数据
String result = serberBr.readLine();
System.out.println(result);
//释放资源
s.close();
}
}
服务端:
package com.itheima_07;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author huan
* @version 创建时间:2020年4月26日 下午10:26:15
*
*/
public class ServerTest {
public static void main(String[] args) throws IOException {
//创建服务端Socket对象
ServerSocket ss = new ServerSocket(8888); //端口要一致,且未被占用
//监听
Socket s = ss.accept();
//获取输入流对象
BufferedReader br= new BufferedReader(new InputStreamReader(s.getInputStream()));
//获取用户名和密码
String username = br.readLine();
String password = br.readLine();
//判断用户名和密码是否正确
boolean flag= false;
if("admin".equals(username) && "password".equals(password)) {
flag = true;
}
//获取输出流对象
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
//返回判断结果
if(flag) {
out.println("登录成功");
}else {
out.println("登录失败,用户名或密码错误");
}
//释放资源
s.close();
//ss.close(); //服务器一般不关闭
}
}
运行时,先启动服务端,再启动客户端
熟练运用eclipse可以减轻很多负担,部分可以参考https://blog.csdn.net/H200102/article/details/105779522