Java学习
idea使用
使用idea自动生成get和set方法
Idea快捷键
-
删除当前行 ctrl+ d
-
添加注释或取消 ctrl+ /
-
自动导包(需要先设置) alt + Enter
-
自动调整代码格式 ctrl + shift+ L
-
运行代码 ctrl+R
-
构造器生成、get、set方法、tostring、在pom.xml文件中快捷配置依赖 alt + insert
-
查看类的层级关系(光标放在该类上) ctrl+ H
-
定位到方法(光标放在方法上) ctrl+B
-
异常处理 alt + ctrl+ t
-
大小写转换 ctrl+ shift+u
-
加入@Test注解解决飘红 Alt + Enter
-
包的重命名 shift + f6
-
多行输入 alt+鼠标左键
-
查看所有的快捷键 ctrl+j=
-
复制当前行到下—行 ctrl+ ↓
-
补全代码 alt+/
-
创建类图 ctrl+Alt+shift+u
this使用细节
-
1.this关键字可以用来访问本类的属性、方法、构造器
-
2.this用于区分当前类的属性和局部变量
-
3.访问成员方法的语法: this.方法名(参数列表)
-
4.访问构造器语法: this(参数列表)﹔注意只能在构造器使用(即只能在构造器中访问另外一个构造器)必须放在第一条语句
-
5.this不能在类定义的外部使用,只能在类定义的方法中使用
封装
封装作用:隐藏对象内部的真实性封装流程如何实现:
-
1.需要将对象属性私有化---外部不能够随意给我们对象属性赋值
-
2.需要提供get/set方法 set方法给我们的成员属性赋值 get获取成员属性的
-
3.提供get/set方法作用:验证参数的合法性
继承
子类只需要继承 就可以获得父类的方法和属性
public class Student extends Parent{}
类图
优缺点
-
1.好处:
- 提高了代码的复用性(多个类相同的成员属性和方法可以放到一个类当中)提高了代码的维护性(如果方法的代码需要修改,只需修改一处即可)。
-
2.弊端:
- 继承让类与类之间产生了关系,类的耦合性也增加了,当父类发生变化时候,子类也不得不跟着变化,削弱了子类的独立性。
-
3.什么是使用继承?
- 假设法:我有两个类A和B,如果满足A是B的一种,或者B是A的一种,就说明他们存在继承关系,这个时候就可以考虑使用继承,否则不能滥用继承。
在子类方法中访问一个变量:
-
1.在我们当前方法局部查找int age = 18。
-
2.在我们当前子类成员属性返回查找。
-
3.如果当前子类成员属性没有就在我们的父类成员属性查找。
-
先查找局部变量→当前类中成员属性变量>父类中→如果类中没有该变量的话直接报错。
想要访问其他类中的属性
System.out.println("访问子类中的年龄"+age);
System.out.println("访问父类中的年龄"+super.age);
- 如果想访问到当前类(子类)中成员属性age如果想访问到当前类(父类)中成员属性age。
- 我们可以使用super关键字是可以访问到父类中成员属性、方法构造函数。
继承中构造方法的访问特性
-
1.子类中所有的构造方法默认都会访问父类中的无参的构造方法。
-
2.因为子类会继承父类中的数据,可能还会使用父类中的数据,所以子类初始化之前,需要对父类进行初始化。
-
3.每个子类构造方法的第一句默认都是:super();
如果父类中没有无参构造方法,只有带参构造方法,怎么办
-
1.通过super关键字显示调用父类的有参构造方法。
-
2.父类中自己单独定义一个无参构造方法。
final关键字
-
由final关键字修饰的变量 类 方法等都不能被改变 但是引用类型成员属性值是可以发生改变的 例如常量。
-
Static关键字
-
被static修饰的为静态
-
特点:被类的所有对象共享访问。
-
非静态成员方法能够访问:1.静态成员变量 2.非静态成员变量3.静态成员方法4.非静态成员方法。
-
静态成员方法能够访问:1。静态成员变量,但是不能够直接访问成员变量2.静态成员方法,但是不能够直接访问成员方法。
-
用途:方便在没有创建对象的情况下进行调用方法或者变量 通过类名.方法名来进行访问。
-
-
Main方法
-
java虚拟机调用 也就是 HotSpot调用。
-
为什么mian方法中有传递String参数 因为方便开发者在运行java程序时注入参数到mian方法中。
-
在main方法中可以直接调用本类的静态方法 如果要访问非静态方法 则需要 new 出对象才能进行调用。
-
多态
-
有继承或者实现的关系
-
有方法的重写 ---子类重写父类
-
有父类的引用指向子类
-
访问特点:
-
成员变量:编译看左边,执行看左边。
-
成员方法:编译看左边,执行看右边。
-
原因:因为方法有重写。但是成员变量没有。
-
-
优缺点:
-
优点:提高程序的扩展性。
-
缺点:不能使用子类的特有功能。
-
多态的转型
向上转型(多态机制)
- 子类到父类:父类引用指向子类对象。
向下转型(强转)
- 父类到子类:父类引用指向子类对象(发生类型转换异常)。
抽象类
注意事项
-
抽象类中不一定有抽象方法,但是有抽象方法的类必须是抽象类。
-
抽象类不能实例化。
-
抽象类由具体的子类进行实例化。
-
子类必须对父类(抽象类)中的抽象方法进行重写。
-
在抽象类中可以定义非抽象方法。
-
子类如果不重写抽象类中的抽象方法,该类还。是抽象类。
-
抽象类中可以有构造方法,用于子类访问父类时的数据初始化。
接口
接口如何定义
public interface Animal {
public abstract void eat();
}
implements接口关键字
public class CatImpl implements Animal
接口成员特点
-
在接口中定义的成员变量都是为常量 默认加上了 public static final关键字进行修饰的。
-
成员方法只能是抽象方法 默认加上了 public abstract。
-
如果需要在接口中定义非抽象方法 需要加上关键字default 在JDK8开始可以在接口中定义非抽象方法。
-
public default void show() {}
-
接口没有构造方法 因为主要对行为进行抽象,没有具体存在,一个类如果没有父类那么会默认继承object类。
类和接口的区别
继承关系
- 类只能单继承但是可以多层继承。接口可以单继承,也可以多继承。
实现关系
- 可以单实现 也可以多实现 还可以继承一个类的同时实现多个接口。
抽象类和接口的关系
-
成员区别
-
抽象类可以定义变量也可以定义常量;有构造方法,抽象方法,非抽象方法。
-
接口定义的是 /常量;抽象方法。
-
-
关系区别
-
类与类 继承和单继承
-
类与接口 实现,单实现和多实现
-
接口与接口 继承,单继承和多继承
-
-
设计区别
-
抽象类 对类抽象,包括属性行为(对事物的抽象)
-
接口 对行为抽象,主要是行为(对行为的抽象)
-
-
内部类
在类中定义的类就是内部类
public class IntClassA{//外部类 public class IntClassB{//内部类 } }
内部类可以直接访问外部类的成员属性
private int age=22; private String name="内部类"; public class IntClassB {//内部类 public void IntClassC(){ System.out.println(age); System.out.println(name); } }
-
在外部类中想使用到内部类 则需要new出内部类 在通过内部类对象引用.内部类方法。
public void IntClassD(){ IntClassB intClassB = new IntClassB(); intClassB.IntClassC(); }
-
分类
-
成员内部类
-
静态内部类
-
方法内部类
-
匿名内部类
-
内部类 成员内部类和局部内部类
-
成员内部类(静态内部类,成员内部类)
-
局部内部类(方法内部类,匿名内部类)
-
区别:
-
如果定义在方法里,该类就是方法内部类或者局部内部类。
-
果定义在方法外面,该类就是为成员内部类,如果加了static关键字则为静态内部类。
-
-
在外部访问内部类
-
格式 new外部类().内部类();
public static void main(String[] args) { /** * 在外界访问内内部类 * 格式 new 外部类().new内部类(); */ IntClassA.IntClassB intClassB = new IntClassA().new IntClassB(); intClassB.IntClassC(); }
-
可以通过private关键字使内部变成私有 则外部类访问不了。
-
静态内部类
-
有static关键字修饰的内部类为静态内部类;
-
有静态关键字修饰的内部类为静态内部类; 静态内部类访问外部类的静态成员变量或方法必须是静态的。
-
访问格式:new MayiktA.MayiktB()。
-
如果需要访问外部类 成员则该成员必须为静态成员。
/** * 在内部类.外部类中的静态方法 * 如果需要访问内部类 则该内部类必须为静态 * 如果需要访问外部类 成员则该成员必须为静态成员 */ public static void show(){ } private static int code; public static class XuexiA{ public void xuexiA() { show(); System.out.println("静态内部类 xuexiA方法"); System.out.println(code); } }
局部内部类
- 局部内部类是在方法中定义的类,所以外界是无法直接使用,需要在方法内部创建对象并使用该类可以直接访问外部类的成员,也可以访问方法内的局部变量。
匿名内部类
实现方式
//匿名内部类
AnonymityInnerClass anonymityInnerClass = new AnonymityInnerClass() {
//重写了 接口中的方法
@Override
public void anonymityInnerClass() {
System.out.println("匿名内部类--接口方法");
}
};
anonymityInnerClass.anonymityInnerClass();
-
使用匿名内部类就不需要创建实现类和子类。
-
直接通过new 内部类的形式 简化不需要创建实现类和子类。
Test test = new Test() { //重写了 抽象类中的方法 @Override public void Test() { System.out.println("匿名内部类--抽象方法"); } }; test.Test();
应用场景 可以简化代码
Animal dog = new Animal() {
@Override
public void eat() {
System.out.println("狗吃骨头");
}
};
dog.eat();
Animal cat = new Animal() {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
};
cat.eat();
- java在编译阶段 底层会创建一个没有名称的实现类 实现new Animal()。
API
api就是java封装的类库。
快捷键ctrl+鼠标左键就可以查看底层源码。
object类
- toString() 目的就是给对象重写的 输出对象使用的成员属性的值。
String类
-
String底层源码就是char [ ] 数组
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[];
-
equals方法如何比较两个字符串的值是否相等
String str1 = "123"; String str2 = "456"; System.out.println(str1.equals(str2));
-
equals源码
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
-
写一个comapre方法 实现equals的作用
public static boolean compare(char[] s1, char[] s2) { if (s1.length == s2.length) { int n = s1.length; int i = 0; while (n-- != 0) { if (s1[i] != s2[i]) { return false; } i++; } return true; } return false; }
-
equals实现登录访问检验
public static void intput() { for (int i = 1; i < 4; i++) { System.out.println("输入你的ID"); Scanner sc1 = new Scanner(System.in); String user = sc1.nextLine(); System.out.println("输入你的密码"); Scanner sc2 = new Scanner(System.in); String userpassword = sc2.nextLine(); System.out.println(login(user, userpassword) ? "输入正确" : "输入错误"+"你还有"+(3-i)+"次机会"); if (login(user, userpassword) == true) { break; } } } public static boolean login(String userid, String userpassword) { String id = "1"; String password = "12"; return id.equals(userid) && password.equals(userpassword); }
-
s1==s2 比较的两个对象的内存地址
-
重写eqluas实现比较两个对象内存地址
@Override public boolean equals(java.lang.Object object) { if (this == object) { return true; } /** * this.getClass()!=s2.getClass() * 两个对象的类型不一致 返回false */ if (object == null || this.getClass() != object.getClass()) { return false; } Student s2 = (Student) object; return this.age == s2.age && this.name.equals(s2.name); }
Instanceof关键字
-
Java中的二元运算符,左边是对象,右边是类或者子类创建的对象时,返回ture;否则返回false。
-
只要是(自身类,自身类父类,object类)都返回ture,其他返回false。
-
instanceof 左边显示声明的类型与右边操作元必须是同种类或者存在继承关系,就是说必须在同一个继承树中否则会编译错误。
Animal cat = new Cat(); if (cat instanceof Animal) { System.out.println("猫 是动物类"); } else { System.out.println("猫 不是动物类"); }
-
如果直接进行强转 会报错
-
在强转时 先判断是否可以强转 可以避免代码运行时抛出异常
if (cat instanceof Dog) { Dog dog = (Dog) cat; } else { System.out.println("该对象引用不是狗类不能强转"); }
遍历字符串
-
两种方法
/** * 实现遍历字符串 */ char[] arr = {'x', 'i', 'a', 'o'}; for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } Scanner scanner = new Scanner(System.in); String str = scanner.nextLine(); for (int i = 0; i < str.length(); i++) { System.out.println(str.charAt(i)); }
-
.repalace()替换方法
String str = "student111Fuck111"; String newStr1 = str.replace("student", "teacher"); String newStr2 = str.replaceFirst("111", "222"); System.out.println(str + "\n" + newStr1+"\n"+newStr2);
-
.split()分割方法
String str = "北京*上海*广州*深圳"; //通过|分割字符串 最终返回数组类型 //使用|,*等符号进行分割时 必须使用转译符“\\“加以转译 String[] arr = str.split("\\*"); for (int i = 0; i < arr.length; ) { System.out.print(arr[i]); i++; }
-
统计大小写字符个数
Scanner scanner = new Scanner(System.in); //使用String接收键盘录入的字符串 String str = scanner.nextLine(); int majuscule = 0; int lowercase = 0; int number = 0; for (int i = 0; i < str.length(); i++) { int n = str.charAt(i); if (n <= 'Z' && n >= 'A') { majuscule++; } else if (n <= 'z' && n >= 'a') { lowercase++; } else if (n <= '9' && n >= '0') { number++; } } System.out.println("大写字母:" + majuscule + "个 小写字母:" + lowercase + "个 数字" + number + "个"); }
-
.indexOf()
String str="XiaoYimingXiaoYiming"; //.length() 是从1开始计算的 //System.out.println("XiaoYiming".length()); //.indexOf() 是从0开始计算的 从输入的字符 Yi 开始查找 如果没有这个字符 返回-1 System.out.println(str.indexOf("Yi",9)); System.out.println((int)(Math.random()*100)+1);
JDK
-
从JDK1.9开始 String类型底层实现从JDK1.8的char[] 改为 byte[] 目的:为了节省String占用的jvm的内存空间
-
JDK1.8 将 方法区/永久代 改名为 元空间
-
JDK1.7 将字符串常量池 从方法区 改为 堆中存放
System类
应用
//给的参数为0时 是正常退出JVM虚拟机 非0时 为异常退出
System.exit(0);
//获取当前系统时间 1970年1月1日---当前时间 返回单位是以毫秒为单位
long s = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
System.out.println("OK");
}
long e = System.currentTimeMillis();
System.out.println("执行该代码花费了" + (e - s) + "毫秒");
工具类的设计思想
将复杂功能封装好 然后直接使用
-
例如数组大小排序
public static void XiaoSort(int[] arr) { Arrays.sort(arr);//sort方法将数组从小到大排序 System.out.println(Arrays.toString(arr)); }
Arrays类
Arrays.toString();方法 可以将数组转化为字符串
int[] arr = {1, 4, 3, 9, 5, 7, 2};
String str = Arrays.toString(arr);
System.out.println(str);
Arrays.sort();方法 将数组用冒泡排序法进行输出
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
XiaoSort(arr);
包装类对原始数据类型进行包装
基本数据类型和包装类的区别
-
1.包装类是对象,拥有方法和字段,对象的调用都是通过引用对象的地址;基本类型不是
-
2.声明方式不同
-
基本数据类型不需要new关键字
-
包装类型需要new在堆内存中进行new来分配内存空间
-
-
3.存储位置不同
-
基本数据类型直接将值保存在值栈中
-
包装类型是把对象放在堆中,然后通过对象的引用来调用他们
-
-
4.初始值不同
-
int的初始值为0,boolean的初始值为false
-
包装类型的初始值为null
-
-
5.包装类是引用传递;基本数据类型是值传递
Integer与int的区别
-
1.int 属于基本数据类型 Integer属于int包装 对象类
-
2.基本数据类型存放在栈空间中--局部变量表
-
3.包装类 属于对象存放在堆空间中
-
4.输出integer 底层会走Integer包装类中的toString方法 属性对应的基本数据类型
Integer integer = 60;//装箱 int a = integer;//拆箱 System.out.println(integer);
-
5.Integer 属于包装类 默认是null int 属于局基本数据类型 默认是0
-
所有的包装类都重写了toString方法 使得 我们引用时不会输出 对象的内存地址而是对象的值
-
String.valueOf(); 将int long double char boolean 直接转换成String
int a = 7; //通过空字符串与int类型相加 String s = "" + a; System.out.println(s); //通过String.valueof()方法进行转换 String s1=String.valueOf(a); System.out.println(s1)
-
将 String类型转换成int类型 不能将除了数字之外的转换为int类型
-
String str="12345"; int i=Integer.parseInt(str); //类名.parse***(str) 将字符串 转换为类名类型 System.out.println(i);
-
Integer.parseInt(s1); 类名.parse***(str) 将字符串 转换为类名类型
Integer.parseInt(str);
拆箱和装箱
装箱:自动将基本数据类型转换为包装类型
- 装箱流程:调用Integer.valueOf(int i)方法
拆箱:自动包装类型转化为基本数据类型
-
拆箱流程:Integer.intvalue()方法获取到Integer 基本数据类型value
-
字符串中的数据排序
String str = "97,16,36,18,50"; String[] s1 = str.split(","); int[] arr = new int[s1.length]; for (int i = 0; i < s1.length; i++) { arr[i] = Integer.parseInt(s1[i]); } Arrays.sort(arr); System.out.println(Arrays.toString(arr));
Date类
获取1970年1月1日00:00:00---到现在的毫秒值
Date date = new Date();
System.out.println(date.getTime());
输出现在的时间 按照定义的规则输出
Date date = new Date();
System.out.println("未格式化日期: " + date);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒");
String newdate = simpleDateFormat.format(date);
System.out.println("格式化后日期: " + newdate);
将时间在 date和String类型中转换
public static void getdate(){
Date date = new Date();
System.out.println("未格式化日期: " + date);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒");
String newdate = simpleDateFormat.format(date);
System.out.println("格式化后日期: " + newdate);
}
public static void changdate(String str) throws ParseException {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
Date date1=simpleDateFormat.parse(str);
System.out.println(date1);
}
系统时间的设置等
Calendar instance = Calendar.getInstance();
//修改获取的系统时间
instance.add(Calendar.YEAR,4);
//设置系统时间
instance.set(1998,4,1);
//获取年月日
int y = instance.get(Calendar.YEAR);
int m = instance.get(Calendar.MONTH);
int d = instance.get(Calendar.DATE);
System.out.println(y + "年" + (m + 1) + "月" + d + "日");
异常处理
-
try...catch
-
使用格式:
-
try{
- 可能出现异常的代码;
-
}catch(异常类名 变量名){
- 异常的处理代码;}
-
-
代码示例
System.out.println("开始");
try {
a();
} catch (Exception e) {
System.out.println(e.toString());
}
System.out.println("结束");
}
Throwable类是Java语言中所有错误和异常的超类。
直接使用Exception来进行捕获所有异常(Exception是继承了Throwable类的)
-
单独捕获的异常必须放在Exception异常之前 不然捕获不到
try { a(); } catch (Exception e) { System.out.println(e.toString()); }
-
e.getMessage()方法可以输出异常原因System.out.println(e.getMessage());)
try { a(); } catch (Exception e) { System.out.println(e.getMessage()); } System.out.println("结束");
-
e.toString()方法会描述异常的简短信息
System.out.println("开始"); try { a(); } catch (Exception e) { System.out.println(e.toString()); } System.out.println("结束");
异常分类
1.编译时异常 :当前java源代码无法编译成.class文件 在编译时发生的异常 idea以红叉展示
-
必须使用try..catch..来捕获异常
-
编译时异常不会继承 RuntimeException类
2.运行时异常 :该.java源代码已经编译成.class文件 java程序在运行时可能发生的异常
-
不需要强制要求使用try..catch..来捕获异常
-
运行时异常会继承 RuntimeException类
异常处理: throws
-
1.向上抛出异常
public static void main(String[] args) throws Exception { a(); } public static void a() throws Exception { String str = "吗23"; int i = Integer.parseInt(str); System.out.println(i); }
-
2.try..catch..来捕获异常
try { a(); } catch (Exception e) { System.out.println(e.toString()); } } public static void a() { String str = "吗23"; int i = Integer.parseInt(str); System.out.println(i); }
自定义处理异常
自定义的异常类 如果要实现编译时异常则继承Exception类 如果要实现运行时异常则继承RuntimeException类
public class LoginException extends Exception{
public LoginException(String message) {
super(message);
}
}
public static void login(String User, String pwd) throws LoginException {
if (!("肖伊明".equals(User) && "123456".equals(pwd))) {
throw new LoginException("输入的ID和密码不正确!");
}
System.out.println("ID和密码正确");
}
递归
自己调用自己
public static int factorial(int n){
if(n == 1){
return 1;
}eles{
return n * factorial(n-1);
}
}
注意事项
-
1.使用递归算法时 需要注意栈空间深度
-
2.减少递归调用深度 通过一定条件退出
-
3.避免栈空间 栈溢出
IO
File
File创建文件
-
mkdir:创建文件夹
-
createNewFile:创建文件 首先判断该文件是否存在 存在返回false 不存在则创建该文件
File判断方法
-
isFile :判断目标是否为文件
-
isDirectory:判断目标是否为文件夹
-
exists 判断目标是否存在
-
绝对路径:直接写死的 如果将该代码放入其他电脑上运行 没有该路径会报错
-
相对路径:多态获取运行项目路径更加灵活
File的删除方法
-
delete();方法
File file = new File("D:\\Test\\day02"); if (file.isDirectory()){ System.out.println("删除多个文件"); deletes(file); } if(file.isFile()){ System.out.println("删除单个文件"); deletem(file); } } public static void deletes(File file){ File[] files = file.listFiles(); for (File f:files){ f.delete(); System.out.println(f.getAbsolutePath()); } file.delete(); } public static void deletem (File file){ file.delete(); }
使用递归删除文件
public static void main(String[] args) {
File file = new File("D:\\Test");
System.out.println(deletes(file));
}
public static boolean deletes(File file){
if (file==null){
return false;
}
File[] files = file.listFiles();
for (File f:files){
if (f.isDirectory()){
deletes(f);
}else {
f.delete();
}
file.delete();
}
return true;
}
IO流
分类
-
根据数据流向分类
-
输入流:读数据 将硬盘中的数据读取到内存当中
-
输出流:写数据 将程序中的数据写入硬盘
-
-
根据数据类型分类
-
字节流:字节输入/输出流
-
字符流:字符输入/输出流
-
-
IO流应用场景
-
纯文本文件,优先使用字符流
-
图片,视频,音频等二进制文件,优先使用字节流
-
不确定文件类型,优先使用字节流,字节流时万能的流
-
OutputStream
FileOutputStream
-
write:写入数据到硬盘当中
FileOutputStream output = new FileOutputStream("D:\\yahu\\da\\Test_2.txt"); output.write('2'); output.close();
-
写入数据换行和追加数据
FileOutputStream f = new FileOutputStream("D:\\yahu\\da\\test.txt", true); //第二个参数为ture 表示追加数据 byte[] arr = {97, 98, 99, 100}; f.write(arr, 1, arr.length - 1); //String str="xiaoyiming"; for (int i = 0; i < 10; i++) { f.write("2222".getBytes()); //换行 f.write("\n".getBytes()); } f.close();
-
Finally:在异常处理时,提供Finally来执行所有清除操作,释放资源
-
read:从该输入流读取一个字节的数据
// int read = f.read(); // while (read != -1) { // System.out.print((char) read); // read = f.read(); // } //优化方案 int read; while ((read = f.read()) != -1) { System.out.print((char) read); }
-
复制数据 使用FileInputStream和FileOutputStream
FileInputStream input = new FileInputStream("D:\\yahu\\da\\test.txt"); FileOutputStream output = new FileOutputStream("D:\\yahu\\da\\Test_2.txt"); int by; while ((by=input.read())!=-1){ //写资源到磁盘中 output.write(by); } output.close(); input.close();
-
使用字节流以字符数组的形式读取数据
FileInputStream f = new FileInputStream("D:\\yahu\\da\\test.txt"); byte[] bytes = new byte[3]; f.read(bytes); System.out.println(bytes);
-
使用循环输出数据
FileInputStream f = new FileInputStream("D:\\yahu\\da\\test.txt"); byte[] bytes = new byte[1024]; int len; while ((len = f.read(bytes)) != -1) { System.out.println(new String(bytes,0,len)); } f.close();
-
字节流复制图片
FileInputStream intput = new FileInputStream("D:\\yahu\\da02\\q1.jpg"); FileOutputStream output = new FileOutputStream("D:\\yahu\\da\\q1.jpg"); byte[] bytes = new byte[10240]; int len; while ((len = intput.read(bytes)) != -1) { output.write(bytes,0,len); } intput.close(); output.close();
字节缓冲流
缓冲区大小默认为8k 即 8192字节
使用缓冲流减少系统调用的频率
字节缓冲流写入数据
/**
* 字节缓冲输出流 和 字节缓冲输入流
*/
FileOutputStream fos = new FileOutputStream("D:\\yahu\\da\\test.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
bos.write("xioayiming".getBytes());
bos.close();
字节缓冲流读取数据
FileInputStream fis = new FileInputStream("D:\\yahu\\da\\test.txt");
BufferedInputStream bis = new BufferedInputStream(fis);
int len;
byte[] bytes = new byte[1024];
while ((len = bis.read(bytes)) != -1) {
System.out.print(new String(bytes, 0, len, "GBK"));
}
bis.close();
字节缓冲流复制视频和字节缓冲流读取数据
-
字节流比缓冲流慢了很多,建议使用缓冲流复制
long s1 = System.currentTimeMillis(); BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\yahu\\da02\\q2.mp4")); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\yahu\\da\\q1.mp4")); int len; byte[] bytes = new byte[1024]; while ((len = bis.read(bytes)) != -1) { bos.write(bytes, 0, len); } long e1 = System.currentTimeMillis(); System.out.println("缓冲区花费:" + (e1 - s1) + "毫秒"); long s2 = System.currentTimeMillis(); FileInputStream fis = new FileInputStream("D:\\yahu\\da02\\q2.mp4"); FileOutputStream fos = new FileOutputStream("D:\\yahu\\da\\q2.mp4"); int b; while ((b = fis.read()) != -1) { fos.write(b); } long e2 = System.currentTimeMillis(); System.out.println("字节流花费:" + (e2 - s2) + "毫秒"); bis.close(); bos.close(); fis.close(); fos.close();
编码和解码
程序默认的编码格式是UTF-8如果在读取编码格式为GBK的文件时使用的不是GBK编码格式则会乱码
/**
* 编码和解码格式必须一致 不然会乱码
*/
String str = "肖伊明";
byte[] bytes = str.getBytes("UTF-8");
System.out.println(new String(bytes, "GBK"));
/**
* 文件为文本类型 首先使用字符流
* 字符流:对字节流进行了包装
*
* 创建输出写入字符流 传递的就是字节流输出流
*/
OutputStreamWriter opsw = new OutputStreamWriter(new FileOutputStream("D:\\yahu\\da\\text.txt"));
opsw.write("肖伊明\n123");
opsw.close();
InputStreamReader ipsr = new InputStreamReader(new FileInputStream("D:\\yahu\\da\\text.txt"));
char[] chars = new char[1024];
int len = ipsr.read(chars);
System.out.println(new String(chars,0,len));
// int len;
// while ((len = ipsr.read(chars)) != -1) {
// System.out.println(new String(chars, 0, len));
// }
ipsr.close();
}
字符流读取数据
-
不需要使用while循环
InputStreamReader ipsr = new InputStreamReader(new FileInputStream("D:\\yahu\\da\\text.txt")); char[] chars = new char[1024]; int len=ipsr.read(chars); System.out.println(new String(chars, 0, len));
-
字符流拷贝文本文件
InputStreamReader ipsr = new InputStreamReader(new FileInputStream("D:\\yahu\\da\\text.txt")); OutputStreamWriter opsw = new OutputStreamWriter(new FileOutputStream("D:\\yahu\\da\\text1.txt")); char[] chars = new char[1024]; int len; while ((len=ipsr.read(chars))!=-1){ opsw.write(chars,0,len); } ipsr.close(); opsw.close();
-
转换流的便捷方式:FileWriter和FileReader
FileReader fileReader = new FileReader("D:\\yahu\\da\\text.txt"); FileWriter fileWriter = new FileWriter("D:\\yahu\\da\\text2.txt"); char[] chars = new char[1024]; int len; while ((len = fileReader.read(chars)) != -1) { System.out.println(new String(chars, 0, len)); fileWriter.write(chars, 0, len); } fileWriter.close(); fileReader.close();
字符缓冲流
-
自动换行写入数据
BufferedReader bufferedReader = new BufferedReader(new FileReader("D:\\yahu\\da\\text.txt")); BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("D:\\yahu\\da\\text3.txt")); char[] chars = new char[1024]; int len; while ((len = bufferedReader.read(chars)) != -1) { System.out.println(new String(chars, 0, len)); bufferedWriter.write(chars, 0, len); } bufferedReader.close(); bufferedWriter.close();
-
自动换行写入数据
BufferedReader bufferedReader = new BufferedReader(new FileReader("D:\\yahu\\da\\text4.txt")); BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("D:\\yahu\\da\\text5.txt")); //以一行一行的形式读取数据 String s; while ((s = bufferedReader.readLine()) != null) { bufferedWriter.write(s); bufferedWriter.newLine(); System.out.println(s); } bufferedReader.close(); bufferedWriter.close();
集合框架
单列 Collexcion(存放单列数据)接口
-
可重复 List:存入数据可重复
-
Arraylist实现类:基于数组数据结构实现(存入数据有序性)
-
Linkedlist实现类:基于链表数据结构实现(存入数据有序性)
-
-
不可重复 Set:存入数据不可重复
-
HashSet实现类:基于Map集合实现 不允许存入重复数据
-
Treeset实现类等
-
双列 Map接口(存入多列数据 key value)
-
HashMap实现类:JDK1.7底层基于数组+链表实现 JDK1.8底层基于数组+链表+红黑树实现
-
Hash Table实现类等
ArrayList集合
方法
/**
* new ArrayList<泛型> 泛型必须为引用类型
* 在集合中 使用<泛型> 必须使用引用类型 如果想使用基本数据类型 则必须使用相对应的包装类
*/
List<String> arrayList = new ArrayList<>();
//向集合中存入元素 定义的泛型类型
arrayList.add("a");
arrayList.add("b");
arrayList.add("c");
arrayList.add("d");
//ArrayList遍历
for (int i = 0; i < arrayList.size(); i++) {
System.out.println("arrayList["+i+"]:"+arrayList.get(i));
}
//ArrayList修改数据
String set = arrayList.set(1, "yi");//返回值为修改先的元素内容
System.out.println("-------------");
for (int i = 0; i < arrayList.size(); i++) {
System.out.println("arrayList["+i+"]:"+arrayList.get(i));
}
System.out.println(set);
String remove = arrayList.remove(1);//返回值为删除前的元素内容
System.out.println("-------------");
for (int i = 0; i < arrayList.size(); i++) {
System.out.println("arrayList["+i+"]:"+arrayList.get(i));
}
System.out.println(remove);
创建学生集合输出
Student student1 = new Student("张三", 20);
Student student2 = new Student("李四", 21);
Student student3 = new Student("王五", 22);
Student student4 = new Student("蚂蚁", 23);
Student student5 = new Student("大象", 24);
ArrayList<Student> students = new ArrayList<>();
students.add(student1);
students.add(student2);
students.add(student3);
students.add(student4);
students.add(student5);
for (Student student : students) {
System.out.println(student.getName() + " " + student.getAge());
}
练习
-
主程序
public static void main(String[] args) { while (true) { System.out.println("1.新增学生" + "\n" + "2.修改学生信息" + "\n" + "3.删除学生信息" + "\n" + "4.查询学生信息" + "\n" + "5.退出系统" + "\n"); System.out.println("输入你的操作"); Scanner scanner = new Scanner(System.in); int options = scanner.nextInt(); switch (options) { case 1: newAdd(); break; case 2: revise(); break; case 3: delete(); break; case 4: inQuire(); break; case 5: System.exit(1); break; } } }
-
查询学号方法
private static ArrayList<Student> student = new ArrayList<Student>(); public static Student getByusIdStudent(String usId, boolean isDelete) { for (int i = 0; i < student.size(); i++) { Student student1 = student.get(i); if (student1.getId().equals(usId)) { if (isDelete) { student.remove(i); } } return student1; } return null; }
-
新增学生信息
public static void newAdd() { System.out.println("新增学生学号"); Scanner id = new Scanner(System.in); String usId = id.nextLine(); Student usIdStudent = getByusIdStudent(usId, false); if (usIdStudent != null) { System.out.println("该学号已被" + usIdStudent.getNaem() + "占用"); return; } System.out.println("新增学生姓名"); Scanner name = new Scanner(System.in); String usName = name.nextLine(); System.out.println("新增学生年龄"); Scanner age = new Scanner(System.in); int usAge = age.nextInt(); student.add(new Student(usId, usName, usAge)); }
-
修改
public static void revise() { System.out.println("输入需要修改的学生学号"); Scanner sc = new Scanner(System.in); String usId = sc.nextLine(); Student byusIdStudent = getByusIdStudent(usId); if (byusIdStudent == null) { System.out.println("没有找到该学生"); return; } System.out.println("输入修改的学生名称"); String newName = sc.nextLine(); System.out.println("输入修改的学生年龄"); int newAge = sc.nextInt(); byusIdStudent.setNaem(newName); byusIdStudent.setAge(newAge); }
-
删除
public static void delete() { System.out.println("输入需要删除的学生学号"); Scanner s4 = new Scanner(System.in); String uid = s4.nextLine(); Student byusIdStudent = getByusIdStudent(uid,true); if (byusIdStudent == null) { System.out.println("没有查找到需要删除的学生"); return; } System.out.println("删除成功"); }
-
查询
public static void inQuire() { System.out.println("学生ID" + "\t\t\t" + "学生姓名" + "\t\t\t" + "学生年龄" + "\t\t\t"); for (Student s : student) { System.out.println((s.getId()) + "\t\t\t\t" + s.getNaem() + "\t\t\t\t" + s.getAge() + "\t\t\t"); } }
迭代器:集合中独有的
-
next方法
-
手写Next方法
private List list; public Test_2(List list) { this.list=list; } int count = 0; public Object next() { if (count >= list.size()) { throw new MException("无法继续向下获取元素"); } return list.get(count++); }
-
List集合概念和特点
-
1.有序集合,用可以精确的控制列表中每个元素的插入位置,可以通过整数索引访问元素,并搜索列表的元素
-
2.与Set集合不同,List集合通常允许重复的元素
-
3.特点:
-
1.有序:存储和取出的元素顺序一致
-
2.可重复:存储的元素可以重复
-
-
ListIterator遍历
ArrayList<String> arrayList = new ArrayList<>(); arrayList.add("a"); arrayList.add("b"); arrayList.add("c"); for (String s : arrayList) { System.out.println(s); } ListIterator<String> stringListIterator = arrayList.listIterator(); while (stringListIterator.hasNext()) { System.out.println(stringListIterator.next()); } System.out.println("----------"); while (stringListIterator.hasPrevious()){ System.out.println(stringListIterator.previous()); }
-
增强for循环
-
for(l类型 变量名 : 数组或者集合){
-
循环体
-
}
-
-
示例
ArrayList<String> arrayList = new ArrayList<>(); arrayList.add("a"); arrayList.add("b"); arrayList.add("c"); for (String s : arrayList) { System.out.println(s); }
-
泛型
- 在编译阶段限制的类型 只能使用指定的类型
泛型类
-
如何定义
public class M_test<T>{ private T t; public T getT() { return t; } public void setT(T t) { this.t = t; } }
-
使用
M_test<String> stringMTest = new M_test<>(); stringMTest.setT("xiao"); System.out.println(stringMTest.getT()); M_test<Integer> integerMTest = new M_test<>(); integerMTest.setT(12); System.out.println(integerMTest.getT());
泛型方法
-
如何定义
public T show(T t){ return t; }
-
使用
M_test2<String> stringMTest2 = new M_test2<>(); System.out.println(stringMTest2.show("xiao")); M_test2<Integer> integerMTest2 = new M_test2<>(); System.out.println(integerMTest2.show(12));
泛型接口
-
如何定义接口
public interface M_test4 <T>{ T show(T t); }
-
如何定义实现类
public class TestImpl<T> implements M_test4<T>{ @Override public T show(T t) { return t; } }
-
使用
TestImpl<String> stringTest = new TestImpl<>(); String xiao = stringTest.show("xiao"); System.out.println(xiao);
泛型类型通配符的上限和下限
/**
* List<? extends M_Parent> list 上限 M_Parent和他的子类 都可以接收
* List<? super Student> list 下限 Student和他的父类 都可以接收
*/
public void printList(List<? extends M_Parent> list) {
}
public void scannerList(List<? super Student> list) {
}
可变参数
-
int... a 可变参数 底层基于数组实现
public static void main(String[] args) { sum(1, 1, 2); } public static int sum(int... a) { int n = 0; for (int i : a) { n += a[i]; } System.out.println(n); return 0; }
-
定义其他变量必须定义在可变参数前
public static int s(int c, int... a) { return 0; }
-
Arrays.asList方法 可以修改 不可以添加和删除 因为在创建时就定义好了元素个数
/** * 使用Arrays.asList方法无法添加和删除 * Arrays.asList 定义好的元数个数不可改变 * 但是可以修改元素 */ List<String> list = Arrays.asList("xiao", "yi", "ming"); list.set(1,"wangmazi"); System.out.println(list);
Vector集合与ArrayList集合的区别
-
ArrayList线程不安全 Vector线程安全
-
ArrayList每次扩容是原来容量的1.5倍 Vector是原来的2倍
-
Vector可以设置每次扩容的值
-
ArrayList是通过懒加载的形式初始化容量 Vector是直接通过构造函数初始化 数组容量=10
ArrayList源码
-
add方法
-
1.判断集合容量是否装的下
-
2.如果装不下则扩容原来数组大小的1.5倍,将原来的属猪拷贝到新的数组中
-
-
get方法
- 直接提供了根据index下标查询 效率高
-
remove方法
- 查找到删除对应的index下标位置+1 到最后index元素值向前移动一位
链表数据结构
单向链表
-
遍历单向链表
public static void showNode(Node<?> node) { Node<?> cnNode = node; while (cnNode != null) { System.out.println(cnNode.v); cnNode = cnNode.next; } }
-
链表数据结构如何新增
public static void addNode(Node<String> endNode, Node<String> newnode) { endNode.next = newnode; }
Node<String> node4 = new Node<>(); node4.v = "d"; addNode(node3, node4);
-
链表结构如何删除
-
将需要删除的元素前一个节点指向当前删除节点的下一个节点
public static void deleteNode(Node<String> lastNode, Node<String> nextNode) { lastNode.next = nextNode; }
deleteNode(node1, node3);
-
LinkedList集合
-
LinkedList add方法
-
public void add(E e) { Node l = last; Node<E> newNode = new Node<>(e, l, null); last = newNode; if (l == null) { first = newNode; } else { l.next = newNode; } size++; }
-
-
LinkedList get方法
-
public E get(int index) { return node(index).item; } public Node<E> node(int index) { if (index < size >> 1) { Node<E> f = first; for (int i = 0; i < index; i++) { f = f.next; } return f; } else { Node<E> l = last; for (int i = size - 1; i > index; i--) { l = l.prev; } return l; } }
-
-
LinkedList remove方法
-
public E remove(int index) { return unLink(node(index)); } private E unLink(Node<E> node) { final E element = node.item;//获取删除元素值 Node<E> prev = node.prev;//删除节点的上一个节点 Node<E> next = node.next;//删除节点的下一个节点 //如果删除节点的上一个节点为空 if (prev == null) { //删除的节点为头节点 first = next; } else { //如果删除的不是头节点 那么当前需要删除的元素的上一个节点的下一个节点就是当前需要删除的元素的下一个节点 prev.next = next; //需要删除的元素的上一个节点则赋值为空 node.prev = null; } if (next == null) { //删除节点为尾节点 last = prev; } else { //如果删除的不是尾节点 那么需要删除的元素的下一个节点的上一个节点就是需要删除的元素的上一个节点 next.prev = prev; //需要删除的元素的下一个节点为空 node.next = null; } node.item = null; size--; return element; }
-
-
手写LinkList总
-
public class TomatoLinkedList<E> { private Node<E> first;//第一个节点 private Node<E> last;//尾节点 int size = 0; int modCount = 0; //LinkedList是基于链表实现的 private static class Node<E> { private E item;//当前节点的值 private Node<E> prev;//当前节点的上一个节点 private Node<E> next;//当前节点的下一个节点 public Node(E item, Node<E> prev, Node<E> next) { this.item = item; this.prev = prev; this.next = next; } } public void add(E e) { Node l = last; Node<E> newNode = new Node<>(e, l, null); last = newNode; if (l == null) { first = newNode; } else { l.next = newNode; } size++; } public E get(int index) { return node(index).item; } public Node<E> node(int index) { if (index < size >> 1) { Node<E> f = first; for (int i = 0; i < index; i++) { f = f.next; } return f; } else { Node<E> l = last; for (int i = size - 1; i > index; i--) { l = l.prev; } return l; } } public E remove(int index) { return unLink(node(index)); } private E unLink(Node<E> node) { final E element = node.item;//获取删除元素值 Node<E> prev = node.prev;//删除节点的上一个节点 Node<E> next = node.next;//删除节点的下一个节点 //如果删除节点的上一个节点为空 if (prev == null) { //删除的节点为头节点 first = next; } else { //如果删除的不是头节点 那么当前需要删除的元素的上一个节点的下一个节点就是当前需要删除的元素的下一个节点 prev.next = next; //需要删除的元素的上一个节点则赋值为空 node.prev = null; } if (next == null) { //删除节点为尾节点 last = prev; } else { //如果删除的不是尾节点 那么需要删除的元素的下一个节点的上一个节点就是需要删除的元素的上一个节点 next.prev = prev; //需要删除的元素的下一个节点为空 node.next = null; } node.item = null; size--; return element; } public static void main(String[] args) { TomatoLinkedList<String> stringTomatoLinkedList = new TomatoLinkedList<>(); stringTomatoLinkedList.add("a"); stringTomatoLinkedList.add("b"); stringTomatoLinkedList.add("c"); stringTomatoLinkedList.remove(1); System.out.println(stringTomatoLinkedList.get(1)); for (int i = 0; i < stringTomatoLinkedList.size; i++) { System.out.println(stringTomatoLinkedList.get(i)); } }
-
Map集合
-
特点
- 映射键值对的形式 key和 value
- key不可以重复但是value可以重复 如果key存在 会直接修改key的值
- Map集合是散列存放数据的 HashMap 无序的 LinkedHashMap 有序的
- Map集合存储顺序与Map集合遍历的顺序是无序的 散列的
-
什么是键值对
- key=M1 value=肖伊明
- key=M2 value=肖伊明
-
put方法
-
HashMap<Integer, String> hashMap = new HashMap<>(); //存放数据 hashMap.put(1,"a"); hashMap.put(2,"b"); hashMap.put(3,"c"); hashMap.put(4,"c");
-
-
remove方法
-
//删除对应键的值 hashMap.remove(1);
-
-
replace方法
-
//修改对应键的值 hashMap.replace(3,"d");
-
-
containsKey方法
-
//判断指定的键是否存在 hashMap.containsKey(1);
-
-
containsValue方法
-
//判断指定的值是否存在 hashMap.containsValue("a");
-
HashSet
-
HashSet没有get方法 底层基于HashMap实现 存放key是散列的 没有index访问元素
-
遍历HashSet集合使用加强for循环
HashSet<String> strings = new HashSet<>(); strings.add("a"); strings.add("b"); strings.add("c"); strings.add("d"); for (String str:strings){ System.out.println(str); }
-
-
HashSet集合不允许元素内容重复
-
存入学生对象不允许重复
HashSet<Student> strings = new HashSet<>(); /** * ((k = p.key) == key || (key != null && key.equals(k)))) * 第一个判断是在判断两个值的内存地址是否相同 显然内存地址不相同 * 执行另一个 equals方法 但是Student并没有重写equals方法 *所以还是在判断两个值的内存地址是否相同都会返回false */ Student s1 = new Student("1", "张三"); Student s2 = new Student("1", "张三"); Student s3 = new Student("2", "李四"); strings.add(s1); strings.add(s2); strings.add(s3); for (Student student : strings) { System.out.println(student); }
-
HashSet去除重复数据 需要重写对应的equals方法和hashCode方法 idea可自动生成 比较两个成员属性值是否相同
@Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Student student = (Student) o; return Objects.equals(age, student.age) && Objects.equals(name, student.name); } @Override public int hashCode() { return Objects.hash(age, name); }
-
-
keySet方法
//获取key值 for (Integer i : hashMap.keySet()) { System.out.println(i); }
-
values方法
//获取集合value值 for (String value : hashMap.values()) { System.out.println(value); }
-
entrySet方法
//获取所有键值对象的集合 for (Map.Entry<Integer, String> entry : hashMap.entrySet()) { System.out.println(entry); }
-
使用方式一遍历HashMap集合
//使用get方法获取value值 for (String key : hashMap.keySet()) { String value = hashMap.get(key); System.out.println(key + "," + value); }
-
使用方式二遍历HashMap集合
//使用entrySet方法实现遍历HashMap集合 for (Map.Entry<String, String> entry : hashMap.entrySet()) { System.out.println(entry.getKey() + "," + entry.getValue()); }
-
使用方式三遍历HashMap集合(使用较少)
//方式三 Set<Map.Entry<String, String>> entries = hashMap.entrySet(); Iterator<Map.Entry<String, String>> iterator = entries.iterator(); while (iterator.hasNext()){ Map.Entry<String, String> next = iterator.next(); System.out.println(next.getKey() + "," + next.getValue()); }
-
HashMap集合存入学生对象
HashMap<Integer, Student> studentHashMap = new HashMap<>(); studentHashMap.put(202401, new Student(21, "张三")); studentHashMap.put(202402, new Student(22, "李四")); studentHashMap.put(202403, new Student(21, "王五")); studentHashMap.put(202404, new Student(23, "张伟")); studentHashMap.put(202405, new Student(24, "张伟")); for (Map.Entry<Integer, Student> studentEntry : studentHashMap.entrySet()) { System.out.println("学号:" + studentEntry.getKey() + "," + studentEntry.getValue()); }
-
HashCode方法重写注意事项
- 如果类中重写了equals方法就必须重写hashCode
- 如果equals方法比较两个对象相等,则HashCode值也一定相等
- 但是两个对象的HashCode值相等不代表使用equals比较也相等
-
Hash冲突
- 如果两个对象的HashCode值相同 但是值不同就会出现Hash冲突
- 后面的对象会将前面的对象覆盖
- jdk1.7在HashMap集合中发生了key hash冲突 使用链表数据结构来存储
-
HashMap键值key可以存入自定义的对象
- 自定义对象必须重写equlas和 HashCode方法
-
使用HashMap集合统计字符中每个字符的出现次数
Scanner scanner = new Scanner(System.in); String str = scanner.next(); HashMap<Character, Integer> hashMap = new HashMap<>(); for (int i = 0; i < str.length(); i++) { Character key = str.charAt(i); Integer value = hashMap.get(key); if (value == null) { value = 1; } else { value++; } hashMap.put(key, value); } for (Map.Entry<Character, Integer> entry : hashMap.entrySet()) { System.out.println("字符" + entry.getKey() + "出现" + entry.getValue() + "次"); }
-
Collections 集合操作工具类
- 只能对单列的集合进行操作
Set集合
-
LinkedHashSet
-
HashSet生成10个不重复的随机数
HashSet<Integer> hashSet = new HashSet<>(); Random random = new Random(); while (hashSet.size() < 10) { int i = random.nextInt(20) + 1; hashSet.add(i); } for (int i : hashSet) { System.out.println(i); }