JavaSe
JDK,JRE,JVM是什么?
JDK:Java Development Kit
JRE:Java Runtime Environment
JVM:Java Virtual Machine
Java跨平台核心是使用Jvm,在不同的操作系统上都是有了java的虚拟机,它通过虚拟机屏蔽了系统底层的差别,真正的实现了一次编译到处运行
编译型,解释型
编译型:对操作系统要求比较低,效率高,将整个程序编译一下就可以用
解释型:低速度要求不高,边执行边解释
Java:即编译型也包含了解释型,
程序运行机制
我们java源代码通过"javac" 预编译,编译成字节码class文件 (java编译器)
然后将我们class文件放在虚拟机中的类装载器里面,这里的话类就加载到jvm里面了,加载后就会通过字节码校验器用来校验你的代码有没有出错,接着就经过解释器(解释器)解释给操作系统(走一步运行一步)然后程序就运行起来了。
Java基础语法
注释
标识符,关键字
数据类型
数据类型扩展
进制
浮点数拓展
char类型扩展
类型转换
变量,常量,作用域
运算符
一元运算符:一个数进行计算,i++,i--
二元运算符:两个数进行计算,a+b,b-a
三元运算符:三个数进行判断,x ? y : z 如果x为true 结果为y x为false 结果为z
i+=2 的意识就算 i=i+2
逻辑运算
三元运算
判断
public class 判断 {
public static void main(String[] args) {
//1.IF判断单条件
int a=10;
if (a>1){
System.out.println("猫");
}else{
System.out.println("狗");
}
//2.if判断多条件区间
//题目:定义一个整数,判断整数大于10输出猫,大于20输出狗,大于30输出蛇,如果都不满足输出虎
int b =5;
if (b>10){
System.out.println("猫");
}else if (b>20){
System.out.println("狗");
}else if(b>30){
System.out.println("蛇");
}else{
System.out.println("虎");
}
//题目:定义一个成绩,如果成绩大于等于90输出优秀,如果成绩大于等于80输出良好,如果成绩大于等于70输入及格,小于70输出不及格
int grade = 80;
if (grade>=90){
System.out.println("优秀");
}else if (grade>=80){
System.out.println("良好");
}else if (grade>=70){
System.out.println("及格");
}else{
System.out.println("不及格");
}
//3.匹配判断 switch,传入一个值,通过case来进行匹配,如果传入的值匹配到相应的case者输出相应的内容
//注意:1.在jdk1.7后支持String类型匹配判断了,
// 2.在case条件后面必须要加上break,否者会出现当前条件满足后还会继续执行下面的判断(穿透)
int c=10;
switch(c){
//进行匹配e,如果e等于case中的值则输出对应的内容
case 5:
System.out.println("虎");
break;
case 10:
System.out.println("年");
break;
case 15:
System.out.println("大");
break;
//图个上面条件都不满足者输出default中的内容
default:
System.out.println("吉");
}
//字符串条件匹配
String d="你好";
switch(d){
//进行匹配e,如果e等于case中的值则输出对应的内容
case "你":
System.out.println("虎");
break;
case "好":
System.out.println("年");
break;
case "你好":
System.out.println("大");
break;
//图个上面条件都不满足者输出default中的内容
default:
System.out.println("吉");
}
}
}
循环
public class 循环 {
public static void main(String[] args) {
//循环,在java中一共有3种循环,while,do while 和for循环,以及jdk5以后新增了一个主要用于数组的增强型for循环
//1. while循环
//求出1+2+3.。。。+100的总和
//解析
int i=0;//初始
int sum=0;//求出的总和
while(i<=100){
sum=sum+i;
i++;
}
System.out.println(sum);
//2.do while循环
//区别:while循环和do while循环有什么区别呢? while循环是先判断条件在进行循环,do while循环是先循环一次在判断条件
int b=0;
int c=0;
do {
c=c+b;
b++;
}while(b<=100);
System.out.println(c);
//3 最重要的for循环
for (int j=0;j<=100;j++){
System.out.println(j);
}
//题目:1.计算0到100之间的基数和偶数的和
//先将奇数的总和
int jishu=0;//定义奇数的合
int oushu=0;//定义偶数的合
for (int ji=0;ji<=100;ji++){//循环一百次
if (ji%2!=0){ //判断是否为奇数
jishu=jishu+ji; //jishu+=ji
}else{ //判断是否为偶数
oushu=oushu+ji;
}
}
System.out.println(jishu+oushu);//最后奇数+偶数的总数5050
//题目2:用while活for循环输出1-1000之间能被5整除的数,并且每行输出3个
for (int f=1;f<=1000;f++){
if (f%5==0){
System.out.print(f+",");
}
//这里判断是用来换行的 如果当前循环的数能够被他的3被整除那么就说明他是第三个数,就直接换行
if (f%(5*3)==0){
System.out.println();
}
}
int w=1;
while (w<=1000){
if (w%5==0){
System.out.print(w+",");
}
if (w%(5*3)==0){
System.out.println();
}
w++;
}
//题目3:打印九九乘法表
for (int y=1;y<=9;y++){
for (int x=1;x<=y;x++){
System.out.print(y+"*"+x+"="+y*x+"\t");
}
//换行
System.out.println();
}
//增强for循环,通常用户数组和集合
int[] s={1,2,3,4,5};
for (int n=0;n<s.length;n++){
System.out.println(s[n]);
}
for (int g :s) {
System.out.println(g);
}
}
}
Break和continue
Break表示强制跳出当前循环但是没有终止程序,程序还是会继续往下执行,只是当前循环跳出去
for (int l=0;l<=100;l++){
System.out.println(l);
if (l>=3){
break;
}
System.out.println("Hello,World");
// 1,2,3 Hello,World
Continue表示在循环中,用于终止某次循环过程,即跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的判定
//输出1-100,将能够被10整除的就转换成换行符
for (int l=0;l<=100;l++){
if (l%10==0){
System.out.println();
continue;//当执行到这里时,就会不执行本次循环
}
System.out.print(l);
}
//123456789
111213141516171819
212223242526272829
313233343536373839
414243444546474849
515253545556575859
616263646566676869
717273747576777879
818283848586878889
919293949596979899
打印三角形
public class HelloWorld {
public static void main(String[] args){
//打印一个三角形
//首先我们先打印第一部分空白
for (int i=1;i<=5;i++){
//空白是从5个4个3个2个1个进行打印
for (int j=5;j>=i;j--){
System.out.print(" ");
}
//这里是打印左半边
for (int x =1; x<=i; x++) {
System.out.print("*");
}
//这里打印后边半边,因为后边部分要从第二行开始加所以这里下标为2
for(int z=2;z<=i;z++){
System.out.print("*");
}
//换行
System.out.println();
}
}
}
/*
*
***
*****
*******
*********
*/
方法
方法重载
重载表示,方法名相同,方法返回值类型不同,方法参数不同(个数不同,类型不同,顺序不同)都表示重载
public class HelloWorld {
public static void main(String[] args){
HelloWorld helloWorld = new HelloWorld();
System.out.println(helloWorld.count(1, 2));//调用的第一个方法,
System.out.println(helloWorld.count(1.1, 2.2));//调用的第二个方法
}
public int count(int x,int y){
return x+y;
}
public double count(double x,double y){
return x+y;
}
}
可变长参数
可变参数表示当我们不知道这个方法中需要多少个参数,我们就可以使用可变长参数
底层就是将我们的可变长参数转为了一个数组,然后通过下标来拿可变长的值
public class HelloWorld {
public static void main(String[] args){
HelloWorld helloWorld = new HelloWorld();
helloWorld.count(1,2,3,4,5);
}
//这里注意:可变长参数必须定义在参数最后一位否者报错
public void count(int x,int... y){
System.out.println("x="+x);
for (int i=0;i<y.length;i++){
System.out.println("y="+y[i]);
}
}
}
x=1
y=2
y=3
y=4
y=5
题目:定义一个可变长方法,求出最大的值
public class HelloWorld {
public static void main(String[] args){
HelloWorld helloWorld = new HelloWorld();
helloWorld.max(1,2,3,4,5,6);
}
//通过可变长参数来进行找出最大的数
public void max(double... x){
if (x.length==0){
System.out.println("您输入的值为空!");
}
double result =x[0];
for (int i=1;i<x.length;i++){
if(x[i]>result){
result=x[i];
}
}
System.out.println(result);
}
}
//6.0
递归
数组
//定义数组方式一
int[] a={1,2,3,4,5,6,7,8,9,10};
//定义数组方式二
int b[]={1,2,3,4,5,6,7,8,9,10};
//定义数组方式三
int[] c=new int[3];
c[0]=1;
c[1]=2;
c[2]=3;
System.out.println(a[1]);
初始化
内存分析
面向对象
一个类即使什么都不写,他也会存在一个方法;
类中可以定义私有priavte属性但是方法中不可以定义private属性
一个方法中创建的属性会在方法执行完消失,但是方法中创建的对象却不会消失
什么是类,什么是对象?
- 类是一个模板:抽象,对象是一个具体的实例
- 方法:定义,调用
- 对应的应用,引用类型,除了八大基本数据类型其他的都是引用类型,对象是通过引用来操作的:栈---->堆,引用就是指向对象的一个地址
- 属性:字段,成员变量,默认初始化:整数类型默认值0,0.0,布尔类型默认为false,引用对象默认值:null,修饰符 属性类型 属性名 = 属性值
- 对象的创建和使用:必须使用new关键字创造对象,并且还需要构造器 Cat xc=new Cat(); 属性 xc.name ,方法xc.max()
- 类:静态属性:属性,动态行为:方法
构造器
- 使用new关键字,本质就是在调用构造器,用来初始化值
- 有参构造,一旦定义了,就必须要要定义无参构造。
内存分析
我们的对象都存放在堆内存区,我们new一个对象 对象中的方法放在栈里面变成引用变量名,引用变量指向堆内存中的对象
Super关键字
继承
方法重写
如果一个子类继承了父类,那么子类就算没写这个方法,调用也是会指向父类的这个方法,如果子类重写了父类的这个方法,那么就会调用子类的方法。
重写
封装
将一个类封装成一个私有的,给外部提供一个可以设置和获取的方法,保证代码的安全性。
多态
创建的对象能够执行那些方法,主要看对象左边的类型,和后边的关系不大。
注意:
- 多态是方法的多态,属性没有多态。
- 父类和子类,有联系,类型转换异常 ClassCastException
- 存在条件:继承关系,方法需要重写,父类引用指向子类,Cat c=new Dog()
- static 方法,属于类,它不属于实例,不可多态
- final 常量;不可以多态
- private 私有,不可多态
instanceof
类型转换,引用类型,判断一个对象是什么类型,判断两个类是否存在父子关系
static
父类静态代码块
子类静态代码块
父类匿名代码块
父类无参构造
子类匿名代码块
子类无参构造
抽象类
异常
自定义异常
java基础进阶
内部类
内部类的分类
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
什么是内部类:在一个类中的内部在定义一个完整的类
定义了一个内部类,那么javac预编译成class文件会变成两个吗? 会的,包含$的字节码文件就表示是内部类生成的
注意:内部类可以访问外部类的的私有成员属性
成员内部类
使用:
package 进阶.内部类;
//先定义一个成员内部类
public class day1 {
private String name="星辰";
private int age=18;
//定义内部类
class day2{
private String Book="java入门";
private float money=12.9F;
//内部类中定义一个普通方法输出外部类的私有属性和内部类的私有属性
public void go(){
System.out.println(name);
System.out.println(age);
System.out.println("=============================");
System.out.println(Book);
System.out.println(money);
}
}
}
package 进阶.内部类;
public class test {
public static void main(String[] args) {
//第一步要先获取到外部类对象才能获取到内部类的对象
day1 day1 = new day1();
//接着通过外部类对象在new到内部类对象
进阶.内部类.day1.day2 day2 = day1.new day2();
//最后调用内部类的方法
day2.go();
}
}
注意
如果我们在内部类定义的属性和外部类同名会优先调用内部类的属性,如果想要调用外部类就需要加上外部类名称.this.属性名
这里需要注意在内部类中不能创建静态属性,但是可以创建静态常量
package 进阶.内部类;
//先定义一个成员内部类
public class day1 {
private String name="星辰";
private int age=18;
//定义内部类
class day2{
private String Book="java入门";
private float money=12.9F;
private String name="ygq";
//这里需要注意在内部类中不能创建静态属性,但是可以创建静态常量
private static int max=11;
private static final int ma1=11;
//内部类中定义一个普通方法输出外部类的私有属性和内部类的私有属性
public void go(){
//这里默认会优先调用内部类的name属性,如果想要调用外部类的name属性 需要使用day1.this.name
System.out.println(day1.this.name);
System.out.println(age);
System.out.println("=============================");
System.out.println(Book);
System.out.println(money);
}
}
}
静态内部类
代码
package 进阶.内部类;
//外部类
public class 静态内部类 {
private String name="星辰";
private int age=18;
//静态内部类
static class day1{
private String name="yyy";
private int age=17;
//静态内部类中的静态属性
private static String address="北京";
public void foot(){
//输出外部类的私有属性
//静态内部类跟普通类一样,获取其他的类的属性都需要先new过来
静态内部类 jt = new 静态内部类();
System.out.println(jt.name);
System.out.println(jt.age);
//输出静态内部类的属性可以直接输出属性名
System.out.println(name);
System.out.println(age);
//获取静态属性,需要类名称.属性名
System.out.println(day1.address);
}
}
}
package 进阶.内部类;
public class test {
public static void main(String[] args) {
//这里如果我们要调用静态的内部类我们不需要先创建外部类对象,我们直接可以通过外部类.静态内部类直接创建
静态内部类.day1 jt=new 静态内部类.day1();
jt.foot();
}
}
局部内部类
局部内部类是在方法的内部创建了一个内部类,如果想要调用局部内部类就需要在方法的内部new这个内部类然后调用内部类的方法
package 进阶.内部类;
//外部类
public class 局部内部类 {
private String name="yyy";
//定义一个普通方法
public void foot(){
int age=18;
//定义局部内部类
class day1{
private String a="aaa";
public void foot2(){
//输出外部类的属性 或 局部内部类.this.name
System.out.println(name);
//输出foot属性 或 this.age
System.out.println(age);
//输出局部内部类属性,这里的a是常量跟 “aaa” 一样了
System.out.println(a);
}
}
//注意:我们直接调用foot方法是没有办法自动加载day1这个局部内部类的,我们必须在这个上级方法中手动创建这个局部内部类对象才可以执行里面的方法
day1 day1 = new day1();
day1.foot2();
}
}
package 进阶.内部类;
public class test {
public static void main(String[] args) {
//这里我们输出局部内部类方法
局部内部类 ju = new 局部内部类();
//这里调用局部内部类的上级方法即可
ju.foot();
}
}
局部内部类也可以实现接口
注意
我们在局部内部类中调用了上级方法的属性会默认给我上级的属性加上final属性标识常量
为什么要将他变成常量呢?
是因为局部变量在方法加载完就消失了,我们在外层调用的时候就已经消失了就肯定调用不了了,所以在jdk1.8中就自动变成常量,而常量却不会在方法加载完就消失。
匿名内部类
常用类
Object类
GetClass
GetHashCode
ToString
在使用toString方法前必须要重写这个类的toString方法,不然打印出来的就算hashcode值
解释:我们这里有一个类,我想调用它的toString方法但是他默认的tostring方法不能输出我想要得到的结果,那我就在那个类里面重写一个tostring方法,这样我们调用tostring方法后就默认调用我们重写的tostring方法了。
equals
equals跟==有什么区别呢?
- 如果我们的对象没有重写equals方法那么调用的就是object的equals方法,object的equalse方法就是使用==做比较的
- ==比较的是两个对象的内存地址,equalse比较的是两个对象的内容是否相同
- 两个对象hashcode相同,那么他的==不一定相同,equals也不一定相同,因为hashcode返回的是一个int值,int值是有取值范围的肯定会重复因此可能会产生,对象不一样,但是hashcode相等的情况
- 两个对象的equals相同说明是同一个对象,那么他的hashcode是相同的
finalize
包装类
问题:我们八大数据类型存放在我们栈内存还是堆内存呢?是栈内存,只有引用类型才存放在堆内存。
类型转换和装箱拆箱
代码
package 进阶.常用类;
//包装类
public class 包装类 {
public static void main(String[] args) {
//装箱和拆箱:吧基本类型转换成一个对象引用类型,吧栈内存的转换到堆内存中
//jdk1.5之前,手动装箱和拆箱
int age=18;
//装箱
//方法一
Integer integer = new Integer(age);
//也可以使用valueof方法
Integer integer2 =Integer.valueOf(age);
System.out.println("手动装箱"+integer);
//拆箱
int num=integer.intValue();
System.out.println("手动拆箱"+num);
System.out.println("===================");
//jdk1.5之后自动装箱和拆箱
//自动装箱
Integer integer1 = age;
System.out.println("自动拆箱"+integer1);
//自动拆箱
int num1=integer1;
System.out.println("自动拆箱"+num1);
}
}
注意:自动装箱和拆箱背后也是帮我们手动装箱和拆箱,本质还是一样的
基本类型转字符串
parsexxx()可以实现字符串和基本类型转换
注意:String类型转换Boolean类型,如果String是true那么boolean就算true,如果String不为true那么全部都为false
package 进阶.常用类;
//包装类
public class 包装类 {
public static void main(String[] args) {
//将基本类型转换为String类型,将String转换为基本类型,这里只列举int类型
//将int转换String类型
int age=18;
//方式一
String num1=age+"";
System.out.println(num1);
//方式二
String num2=Integer.toString(age);
System.out.println(num2);
//方式三,转换为字符串并且进制,radix:几进制
String num3=Integer.toString(age,2);
//将String转换为int类型
String str = "20";
int str1 = Integer.parseInt(str);
System.out.println(str1);
//注意:String类型转换Boolean类型,如果String是true那么boolean就算true,如果String不为true那么全部都为false
}
}
整数缓冲区
注意:自动包装类会在编译的时候自动帮我们使用Integer.valueof方法,这个方法会判断我们传递的值如果是-128到127之间那么就从缓冲区拿,如果不包含-128到127那么就new Integer
package 进阶.常用类;
//包装类
public class 包装类 {
public static void main(String[] args) {
//面试题
//是因为在堆内存中创建了两个对象,并不是一个对象
Integer integer1 = new Integer(100);
Integer integer2 = new Integer(100);
System.out.println(integer1==integer2);//false
//这里为什么等于true是因为他调用的是Integer.valueof方法,他实现的是-128到127之间会从他已经定义好了的缓冲区拿,
// 这里他两个拿的是同一个缓冲区的东西,那肯定是一个对象就会返回true
Integer integer3=100;
Integer integer4=100;
System.out.println(integer3==integer4);//true
//这里跟上面一样的方法为什么会返回false是因为Integer.valueof方法判断你的值不是-128到127就默认new Integer()
Integer integer5=200;
Integer integer6=200;
System.out.println(integer5==integer6);//false
}
}
String
字符串是常量,创建之后不可改变。
原因:我们在创建一个String name =“hello" 这里就会在我们栈里面创建一个name的引用类型,然后会在我们常量池里面开辟了一块空间里面存放者hello,这里栈的name就引用到我们常量池中的hello空间,但是如果我们将name=”zhangsan“,表面我们看的是重新给name赋值或者修改了,但是在我们的常量池中并没有吧我们hello这块空间修改,而是又开辟了一块空间里面存放着shangsan,然后我们的name引用就会修改成zhangsan这块常量的空间。这时我们又创建了一个Srting name2=”zhangsan“ 那么也会现在栈内存创建一个name2的引用名称,然后并不会在常量池创建一个新的zhangsan而是会继续引用到原来的shangsan这块空间,达到循环利用,如果常量池中没有用到的空间系统会自动清理
String str = new String("java")创建了几个对象?
答案:是两个,首先会在栈里面创建一个变量名称,堆里面创建一个对象,也会在字符串常量池里面存放一个对象,这样就是两个对象了
常用方法
subString(下标) 截取字符串 ;将字符串下标开始截取到结尾例如 abcdef substring(0) bcdef
subString(起始下标,结束下标),abcdef substring(0,3) def
package 进阶.常用类;
//包装类
public class 包装类 {
public static void main(String[] args) {
String name=" 我爱,java,php";
System.out.println("获取字符串长度:"+name.length());
System.out.println("根据下标获取字符串内容"+name.charAt(1));
System.out.println("判断字符串包含某个字符串"+name.contains("y"));
char [] str =name.toCharArray();
for (int i = 0; i < str.length; i++) {
System.out.print(str[i]);
}
System.out.println("将字符串转换为数组"+name.toCharArray());
System.out.println("查找xxx首次出现的位置,如果存在就返回下标,不存在返回-1"+name.indexOf("p"));
System.out.println("查询xxx最后一次出现的位置下标"+name.lastIndexOf("p"));
System.out.println("去掉字符串前后的空格"+name.trim());
System.out.println("将字符串小写转换大写"+name.toUpperCase());
System.out.println("判断字符串是否已xxx结尾"+name.endsWith("php"));
System.out.println("将旧字符串替换新的字符串"+name.replace("php","Python"));
String[] str2=name.split(",");
for (int i = 0; i < str2.length; i++) {
System.out.print(str2[i]);
}
}
}
/*
获取字符串长度:12
根据下标获取字符串内存我
判断字符串包含某个字符串false
我爱,java,php将字符串转换为数组[C@754ba872
查找xxx首次出现的位置,如果存在就返回下标,不存在返回-1 9
查询xxx最后一次出现的位置下标11
去掉字符串前后的空格我爱,java,php
将字符串小写转换大写 我爱,JAVA,PHP
判断字符串是否已xxx结尾true
将旧字符串替换新的字符串 我爱,java,Python
我爱javaphp
*/
补充
案例
package 进阶.常用类;
//包装类
public class 包装类 {
public static void main(String[] args) {
String name="this is a text";
//1
String[] str= name.split(" ");
for (int i = 0; i < str.length; i++) {
System.out.println(str[i]);
}
//2
System.out.println(name.replace("text", "practice"));
//3
System.out.println(name.replace("text", "easy text"));
//4
for (int i = 0; i < str.length; i++) {
char a=str[i].charAt(0);
String b=String.valueOf(a).toUpperCase();
String c=b+str[i].substring(1);
System.out.print(c+" ");
}
}
}
可变字符串
StringBuffer和StringBuilder功能是一样的,只是一个效率低(适合多线程)一个效率高(实现单线程)
常用4个方法:
- append追加
- insert插入
- replace替换
- delete清除
BigDecimal
这是因为浮点数他存放的是近视值
BigDecimal进行加减乘除
- add 加法
- subtract减法
- multiply乘法
除法
Date
Calendar
SimpleDateFormat
System
总结
IO流
介绍
什么是流
流的分类
方向:
输入:将我们硬盘文件中的内容发送到内存中
输出:将我们内存的中内容发送到硬盘中
单位:
字节流
单个字节读取
这里的data读取的是文件中的字节编码ASCII码,我们将ASCII码转换成char类型就可以看到对于的字符
package 字节流;
import java.io.FileInputStream;
import java.io.IOException;
public class 字节读取 {
public static void main(String[] args) throws IOException {
//单个字节读取
//1.先创建一个字节输入流对象读取我们d盘下的abc.txt
FileInputStream fileInputStream = new FileInputStream("d:abc.txt");
//我们创建一个接收读取数据的变量
int message=0;
//循环读取
//read方法返回-1就表示读取完了,我们判断如果read=-1就结束
while ((message=fileInputStream.read())!=-1){
//循环读取,读取一次就打印一次,这里默认打印的是读取的ASCII编码,我们使用char进行转换即可
System.out.print((char)message);
}
//读取完成我们关闭流
fileInputStream.close();
}
}
多个字节读取
package 字节流;
import java.io.FileInputStream;
import java.io.IOException;
public class 字节读取 {
public static void main(String[] args) throws IOException {
//多个字节读取
FileInputStream fileInputStream = new FileInputStream("d:abc.txt");
//这里的思路是,我们创建一个数组,我们将读取的数据存放进数组
//先创建一个数组大小为1024表示一次读取1024个字节
byte[] byte1 =new byte[1024];
//这里定义一个读取了多少字节的变量
int count=0;
while ((count=fileInputStream.read(byte1))!=-1){
//这里打印数组中下标为0到count(读取字节总数)
System.out.printf(new String(byte1,0,count));
}
}
}
输出
将我们程序内存中的数据输出(写入)到硬盘中
package 字节流;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class 字节输出 {
public static void main(String[] args) throws IOException {
//创建一个字节输出流
FileOutputStream fileOutputStream = new FileOutputStream("d:abc.txt",true);
//这里使用write方法是直接重新覆盖原来的内容,如果不想覆盖那就在fileoutputstream构造方法中填true
//这里使用write方法进行写入到文件中,参数为int类型的ASCII编码
fileOutputStream.write(97);
//这里我们使用‘’自动转换为nunicode编码
fileOutputStream.write('2');
//想一次写入多内容
String mes="中国你好";
//mes.getBytes()将字符串转换成字节数组
fileOutputStream.write(mes.getBytes());
//关闭
fileOutputStream.close();
}
}
文件复制
package 字节流;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.UUID;
/*
* 使用字节流来复制文件
* 思想:先读取硬盘A中文件的字节然后输出到硬盘B中去
* */
public class 文件复制{
public static void main(String[] args) throws Exception {
//首先获取输入端文件
FileInputStream fileInputStream = new FileInputStream("D:\\tupian.jpg");
//输出端
FileOutputStream fileOutputStream = new FileOutputStream("E:\\"+UUID.randomUUID()+".jpg");
//一次性获取字节大小
byte[] byt=new byte[1024];
//接受读取次数
int count=0;
//循环一边读取一边输出
while((count=fileInputStream.read(byt))!=-1){
//一次性读取byt字节,从下标0开始,到count结束
fileOutputStream.write(byt,0,count);
System.out.println(count);
}
//关闭流
fileOutputStream.close();
fileInputStream.close();
}
}
字节缓冲流
BufferedInputStream
实际上缓冲流内部是有一个缓冲区
package 字节流.缓冲流;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
/**
* 缓冲字节流
*/
public class buff {
public static void main(String[] args) throws Exception{
//缓冲字节流是普通字节流的增强版,在字节流上套了一层buff
//创建一个普通的字节流
FileInputStream fileInputStream = new FileInputStream("D:\\abc.txt");
//创建一个增强字节流套上普通字节流
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
int count=0;
while ((count=bufferedInputStream.read())!=-1){
System.out.print((char)count);
}
bufferedInputStream.close();
}
}
自定义缓冲区
package 字节流.缓冲流;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
/**
* 缓冲字节流
*/
public class buff {
public static void main(String[] args) throws Exception{
//缓冲字节流是普通字节流的增强版,在字节流上套了一层buff
//创建一个普通的字节流
FileInputStream fileInputStream = new FileInputStream("D:\\abc.txt");
//创建一个增强字节流套上普通字节流
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
//自定义缓冲区
byte[] bytes = new byte[1024];
int count=0;
while ((count=bufferedInputStream.read(bytes))!=-1){
System.out.print(new String(bytes,0,count));
}
bufferedInputStream.close();
}
}
BufferedOutputStream
package 字节流.缓冲流;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
/**
*
*/
public class output {
public static void main(String[] args) throws Exception{
FileOutputStream fileOutputStream = new FileOutputStream("d:abc.txt");
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
String msg="武汉北京青岛";
bufferedOutputStream.write(msg.getBytes());
// 可以不写filsh因为我们在close关闭流对象的时候会自动帮我们flush
bufferedOutputStream.flush();
bufferedOutputStream.close();
}
}
对象流
序列化:将我们内存中的对象写入到硬盘中
反序列化:读取我们硬盘中的对象到我们内存中
注意:
/**
* 注意事项:
* 1.序列化对象必须要实现Serializable接口
* 2.序列化类中对象属性要求实现Serializable接口 例如: private Cat cat; 这里Cat指定的对象必须也有序列化
* 3.序列化版本号ID,保证序列化的类和反序列化是同一个类,如果没有手动去写id那么会自动生成一个id
* 4.使用transient(瞬间的)修饰属性,这个属性不能序列化
* 5.静态属性不能序列化
* 5.序列化多个对象,可以借助集合实现
*/
序列化
package 字节流.序列化;
import java.io.FileOutputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
/**
* 序列化对象写出到文件中
*/
public class fan {
public static void main(String[] args) throws Exception{
FileOutputStream fileOutputStream = new FileOutputStream("d:aaa.bin");
//创建对象流
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
//注意:一点要给序列化的对象实现 implements Serializable
Student student = new Student("张三", 18);
//将对象写出
objectOutputStream.writeObject(student);
//关闭
objectOutputStream.close();
}
}
反序列化
package 字节流.序列化;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
/**
* 反序列化对象(重构成一个对象)
*/
public class fan {
public static void main(String[] args) throws Exception{
//创建一个输入流
FileInputStream fileInputStream = new FileInputStream("d:aaa.bin");
//创建对象输入流进行反序列化
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
//读取对象,进行强转成你需要的对象
Student student=(Student)objectInputStream.readObject();
//关闭
objectInputStream.close();
//打印对象
System.out.printf(student.toString());
}
}
字符流
我们用字节流去读取一个文件里面的中文,会发现乱码。
字符编码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4SiHwCAM-1658980669673)(C:\Users\h\AppData\Roaming\Typora\typora-user-images\image-20220209102401868.png?lastModify=1644400031)]
package 字符流;
import java.io.FileReader;
public class 文件字符流 {
public static void main(String[] args) throws Exception{
FileReader fileReader = new FileReader("d:abc.txt");
//因为这里是字符流所以要用字符类型char
char[] chars= new char[1024];
int count;
//注意:这里读取的不是字节,读取的是字符
while ((count=fileReader.read(chars))!=-1){
System.out.print(new String(chars,0,count));
}
}
}
FileWriter fileWriter = new FileWriter("d:abc.txt");
fileWriter.write("sadmsmicmscosdoicmIC毛idmc扫地出门是多么出色的");
fileWriter.close();
字符流文件复制
package 字符流;
import java.io.FileReader;
import java.io.FileWriter;
public class 文件复制 {
public static void main(String[] args) throws Exception{
FileReader fileReader = new FileReader("d:abc.txt");
FileWriter fileWriter = new FileWriter("E:\\abc.txt");
int count;
while ((count=fileReader.read())!=-1){
fileWriter.write(count);
}
fileWriter.close();
fileReader.close();
}
}
字符缓冲流
BufferedReader
这里增加了一个readLine方法,可以一行一行的读取数据
package 字符流.字符缓冲流;
import java.io.BufferedReader;
import java.io.FileReader;
/**
* 使用字符缓冲流读取文件
*/
public class Buffered {
public static void main(String[] args) throws Exception{
//创建一个普通的字符流
FileReader fileReader = new FileReader("d:abc.txt");
//创建一个增强的字符流套入普通字符流上
BufferedReader bufferedReader = new BufferedReader(fileReader);
//方式一
int count;
char[] chars=new char[1024];
while ((count=bufferedReader.read(chars))!=-1){
System.out.print(new String(chars,0,count));
}
//方式二:一行一行读取
String link;
while ((link=bufferedReader.readLine())!=null){
System.out.println(link);
}
}
}
BufferedWriter
package 字符流.字符缓冲流;
import java.io.BufferedWriter;
import java.io.FileWriter;
/**
* 使用BufferedWriter写出数据
*/
public class bufferedWriter {
public static void main(String[] args) throws Exception{
FileWriter fileWriter = new FileWriter("d:abc.txt");
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
bufferedWriter.write("中国");
//使用增强字符流提供的newLine方法换行
bufferedWriter.newLine();
//也可以使用\n来换行
bufferedWriter.write("中国\n中国");
//刷新流
bufferedWriter.flush();
//关闭
bufferedWriter.close();
}
}
打印流
PrintWriter
public static void main(String[] args) throws Exception{
PrintWriter writer = new PrintWriter("d:abc.txt");
writer.println(97);
writer.println("中国");
writer.println(true);
writer.close();
}
转换流
将字节和字符之间进行转换
字节FileInputStream套进InputStreamReader中就可以将字节流转换成字符流
InputStreamReader
public static void main(String[] args) throws Exception{
/**
* sInputStreamReader读取文件,指定使用的编码
*/
//创建一个字节流输入流对象
FileInputStream fileInputStream = new FileInputStream("d:abc.txt");
//将字节流套进去变成字符流
//这里可以指定读取的文件的编码,如果文件是gbk编码,我们就可以设置gbk
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream,"utf-8");
int count;
while ((count=inputStreamReader.read())!=-1){
System.out.print((char)count);
}
}
OutputStreamWriter
/**
* 使用OutputStreamWriter写出文件,并指定编码格式
*/
//创建一个字节流输出流对象
FileOutputStream fileOutputStream = new FileOutputStream("d:abc.txt");
//将字节流套进去变成字符流
//这里可以指定写出的文件的编码,如果我们想将文件写出格式为utf-8,我能就写utf-8
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream,"utf-8");
outputStreamWriter.write("sdcsdcnsdncsdnc第三次尼斯多久才能");
outputStreamWriter.flush();
outputStreamWriter.close();
File类(重要)
File类是对文件的操作
public static void main(String[] args) throws Exception{
File file = new File("D:\\ccc.txt");
//创建一个新的文件
// file.createNewFile();
//创建一个新的文件夹
// boolean mkdir = file.mkdir();
//清除文件或空目录
// file.delete();
//判断File对象中的文件或者目录是否存在
// file.exists();
//获取文件的绝对路径
// file.getAbsolutePath();
//获取文件名称
// file.getName();
//获取文件所在目录
// file.getParent();//d:
//判断是否是目录
// file.isDirectory();//false
//判断是否为文件
// file.isFile();//true
//获取文件长度
// file.length();//0
//列出目录中所有的文件
// File[] files = file.listFiles();
// for (File file1 : files) {
// System.out.println(file1);
// }
/**
* D:\xcwl\.gitignore
* D:\xcwl\.idea
* D:\xcwl\.mvn
* D:\xcwl\HELP.md
*/
//修改文件名为,这里一定要写绝对路径名,d:xxx
// file.renameTo(new File("d:c.txt"));
}
FileFilter接口
public static void main(String[] args) throws Exception{
//打印指定目录下的文件
File file = new File("D:\\Typora");
//调用file.listFiles方法传递进去FileFilter对象
File[] files = file.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
//这里判断筛选条件
//判断如果文件名后缀为.dll表示将文件路径添加到数组中
if (pathname.getName().endsWith(".dll")){
return true;
}
return false;
}
});
//打印数组中的数据
for (int i=0;i<files.length;i++){
System.out.println(files[i].getName());
}
}
递归打印清除
public static void main(String[] args) throws Exception{
dir(new File("D:\\IO测试"));
}
public static void dir(File file){
//先获取当前文件夹中第一级的文件和目录
File[] files = file.listFiles();
//然后判断是否为空
if (files!=null&&files.length>0){
//循环遍历文件
for (File file1 : files) {
//如果当前遍历的文件是一个文件夹
if (file1.isDirectory()){
//那么继续调用当前方法将文件夹传递进去
dir(new File(file1.toString()));
}
//如果当前不是文件夹是文件那么就直接清除
System.out.println(file1.getName()+"清除"+file1.delete());
}
}
}
Properties
跟IO流相关
public static void main(String[] args) throws Exception{
//创建一个Properties集合
Properties properties = new Properties();
//设置集合属性
properties.setProperty("username","admin");
properties.setProperty("password","123456");
//通过集合的key打印value
System.out.println(properties.get("username"));
//打印集合所有属性
System.out.println(properties.toString());
//3.遍历
//3.1-----keySet
//3.2-----entrySet
//3.3-----stringPropertiesNames()//获取properties的key
Set<String> pro = properties.stringPropertyNames();
for (String key : pro) {
System.out.println(key+"===="+properties.get(key));//我们可以通过这里获取的所有的key来传递给properties.get传递进去key就可以获取value
}
//4.跟流相关的
//-----list方法
//这里是将我们properties中的key和value都打印进我们指定的文件
//创建一个打印流
PrintWriter writer = new PrintWriter("D:abc.txt");
//调用list方法传递进去我们创建的打印流
properties.list(writer);
writer.close();
//-----store方法 保存我们的集合到文件中
FileOutputStream fileOutputStream = new FileOutputStream("d:store.properties");
properties.store(fileOutputStream,"注释");
fileOutputStream.close();
//-----read方法,读取文件集合
//创建一个字节输入流
FileInputStream fileInputStream = new FileInputStream("d:store.properties");
properties.load(fileInputStream);
System.out.println(properties.toString());//{password=123456, username=admin}
}
Store方法保存集合到文件中
FileOutputStream fileOutputStream = new FileOutputStream("d:store.properties");
properties.store(fileOutputStream,"注释");
fileOutputStream.close();
load方法读取文件中的集合
//-----read方法,读取文件集合
//创建一个字节输入流
FileInputStream fileInputStream = new FileInputStream("d:store.properties");
properties.load(fileInputStream);
System.out.println(properties.toString());//{password=123456, username=admin}
IO总结
字节流和字符流FileInputStream和FileOutputStream 他们都是继承的抽象类是InputStream和OutputStream
我们还可以使用对象流来输入输出对象但是这个对象必须要实现Serializable接口
File对象值的是我们硬盘中的一个文件或一个文件夹
集合
概念
对象的容器,定义了对多个对象进行操作的常用方法,可实现数组的功能
和数组的区别
1.数组长度固定,集合长度不固定可以自定义的
2.数组可以存储基本类型和引用类型,但是集合只能存储引用类型(对象)
集合位于位置:java.util.*包下
Collection
概念
collection是所有的根接口,collection下面又有两个子一个是list和set接口
List接口特点:有序(存的顺序和取的顺序是一致的),有下标(跟数组一样),元素可重复(可以重复存同样的元素)
Set接口特点:无序,无下标,元素不可以重复
removeAll:清除传递的集合中有相同的元素,例如a.remove(b) a里面有1,2,3,4,5 ;b里面有2,4,5 那么就会将a里面的2,4,5清除掉
retainAll:清除传递的集合中不包含的元素,例如a.remove(b) a里面有1,2,3,4,5 ;b里面有2,4,5 那么就会将a里面的1,3清除掉
Collection添加普通元素
Collection有序,【无下标】,可以重复
package 进阶.集合.collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
* Collection
* 添加元素
* 清除元素
* 遍历元素
* 判断
*/
public class demo1 {
public static void main(String[] args) {
Collection collection = new ArrayList();
// * 添加元素
collection.add("zhangsan");
collection.add("lisi");
collection.add("wangwu");
// System.out.println(collection.size());//打印元素个数
// System.out.println(collection.toString());//打印集合元素
// * 清除元素
// collection.remove("wangwu");
// System.out.println(collection);//直接打印对象也可以将集合中的元素打印出来
// * 遍历元素
//1.使用增强for循环
for (Object object : collection) {
System.out.println(object);
}
//2.使用迭代器iterator方法(专门用来遍历集合元素的一种方式)
//hasNext()判断是否还有下一个元素
//Next()获取下一个元素
//remove清除当前元素
Iterator iterator = collection.iterator();
while (iterator.hasNext()){
//通过next方法来获取遍历元素,这里可以进行强转
String next = (String)iterator.next();
System.out.println(next);
//在我们迭代器中清除某个元素我们不能使用Collection.remove,否者会抛异常
// 而是使用迭代器中的.remove,原因是迭代器在运行时不允许别人来插手
if (next.equals("lisi")){
iterator.remove();
}
}
//这里我们打印集合,里面就没有lisi这个元素了
System.out.println(collection);
// * 判断
System.out.println(collection.contains("wangwu"));
}
}
Collection添加对象
问题:我在清除里面创建了一个跟集合元素里面一模一样的对象,为什么清除不了我集合中的对象呢?
原因:我们每个对象创建都是在堆里面开辟了一块空间,而集合中存的都是对象的内存地址,所以你新创建的对象的地址跟集合中存的地址不一样。所以清除不掉。
package 进阶.集合.collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
* Collectiond对 对象的操作
* 添加元素
* 清除元素
* 遍历元素
* 判断
*/
public class demo1 {
public static void main(String[] args) {
Collection collection = new ArrayList();
//添加对象
Student student = new Student("admin1", 18);
Student student2 = new Student("admin2", 18);
Student student3 = new Student("admin3", 18);
collection.add(student);
collection.add(student2);
collection.add(student3);
collection.add(student3);//我们list集合接口中是运行重复添加的
System.out.println(collection.toString());
//清除对象
// collection.remove(student);//清除单个元素对象
// collection.clear();//清除全部元素
//遍历对象
//1.增强for循环
// for(Object obj:collection){
// System.out.println(obj);
// }
//迭代器遍历
// Iterator iterator = collection.iterator();
// while (iterator.hasNext()){
// System.out.println(iterator.next());
// }
//判断对象是否存在
collection.contains(student);
//判断集合是否为空
Collection collection1=new ArrayList();
collection1.isEmpty();
}
}
List集合
List集合是Collection的子接口
List集合不能存放基本数据类型,如果我们存放基本数据类型,他内部会自动转换成包装类
List基本使用1
package 进阶.集合.collection.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
/**
* List接口使用
*/
public class demo1 {
public static void main(String[] args) {
List list = new ArrayList();
//新增元素
list.add("苹果");
list.add("菠萝");
list.add("西瓜");
//// System.out.println(list.toString());
// //通过下标来添加元素
// list.add(0,"西红柿");
//// System.out.println(list.toString());
//
// //清除元素
// //通过元素清除
//// list.remove("西瓜");
// //通过下标清除
//// list.remove(0);
//// System.out.println(list.toString());
//
// //遍历
// //1.使用普通for循环来遍历
//// for (int i=0;i< list.size();i++){
//// System.out.println(list.get(i));
//// }
// //2.使用增强for循环遍历
//// for (Object object:list){
//// System.out.println(object);
//// }
// //3.迭代器
//
// //3.1使用普通迭代器
//// Iterator iterator = list.iterator();
//// while (iterator.hasNext()){
//// System.out.println(iterator.next());
//// }
//
// // 3.2通过List新增迭代器遍历
//// ListIterator listIterator = list.listIterator();
// //正向遍历
//// while (listIterator.hasNext()){
//// System.out.println(listIterator.nextIndex()+":"+listIterator.next());
//// }
// //逆向遍历
//// while(listIterator.hasPrevious()){
//// System.out.println(listIterator.previousIndex()+":"+listIterator.previous());
//// }
//
}
}
List基本使用2
注意,清除Integer使用下标或者new Integer()
package 进阶.集合.collection.List;
import java.util.ArrayList;
import java.util.List;
/**
* List的使用2
*/
public class demo2 {
public static void main(String[] args) {
//注意:
//集合存的都是对象的内存地址,那么我们int类型为什么可以存放进去呢?
//因为list内部自动帮我们将int转换成了new Integer()
List list = new ArrayList();
list.add(10);
list.add(20);
list.add(30);
list.add(40);
//int类型内部自动帮我们转换成
list.add(new Integer(50));
System.out.println(list.toString());
//清除:
//List清除Integer使用下标清除或者new Integer()
list.remove(0);
System.out.println(list.toString());
list.remove(new Integer(50));
System.out.println(list.toString());
}
}
List实现类
LinkedList链表是什么:
ArrayList
package 进阶.集合.collection.List.ArrayList;
import 进阶.集合.collection.Student;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
/**
* ArrayList使用
* 1.底层数组实现,有下标,可以重复元素
*/
public class demo1 {
public static void main(String[] args) {
//添加
ArrayList arrayList = new ArrayList();
Student student = new Student("zhangsan", 18);
Student student2 = new Student("lisi", 19);
Student student3 = new Student("wangwu", 20);
arrayList.add(student);
arrayList.add(student2);
arrayList.add(student3);
System.out.println(arrayList.toString());
//清除
//1.通过对象名称清除
arrayList.remove(student);
System.out.println(arrayList.toString());
//2.通过new对象来清除,remove方法内部调用了equals方法,我们在Student对象中重写equals方法即可
arrayList.remove(new Student("zhangsan",18));
System.out.println(arrayList.toString());
//遍历
//1.使用普通迭代器
Iterator iterator = arrayList.iterator();
while (iterator.hasNext()){
Student next = (Student)iterator.next();
System.out.println(next);
}
//2.使用列表迭代器
ListIterator listIterator = arrayList.listIterator();
//正序
while (listIterator.hasNext()){
Student next = (Student)listIterator.next();
System.out.println(next);
}
// //倒序
while (listIterator.hasPrevious()){
Student previous = (Student)listIterator.previous();
System.out.println(previous);
}
//判断
System.out.println(arrayList.isEmpty());//判断当前集合是否为空
System.out.println(arrayList.contains(new Student("zhangsan",18)));//判断当前对象是否在集合中存在
//查找
System.out.println(arrayList.indexOf(new Student("zhangsan",18)));//查询当前对象在集合中的下标
}
}
ArrayList源码
源码分析:
我们创建一个ArrayList对象
ArrayList arrayList = new ArrayList();
它默认调用的是ArrayList中的无参构造,这里的elementData和DEFA......常量都是空的默认的大小肯定是0
private int size;//这里它也创建了一个size 0
private static final int DEFAULT_CAPACITY = 10;//这里默认创建了一个容量大小 常量为10,在我们add方法中会用到
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//null 0
transient Object[] elementData; // 这里的transient类型指的是改参数不能被序列话 null 0
public ArrayList() {
//0=0;
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
所以我们在创建一个ArrayList默认的大小为0
【重点】Add()方法
arrayList.add("中国");
add方法底层
//这里传递的是一个泛型,E表示创建ArrayList时指定的泛型<E>如果不指定默认是Object
public boolean add(E e) {
//这里调用了ensureCapacityInternal并传递了size+1 这里默认是0+1
ensureCapacityInternal(size + 1);
//将参数e赋值给elementData[size+1]
//最后再看这行代码 此时elementData的大小为10 这里size++表示先将当前大小的size赋值进去然后执行完成在+1
//elementData[0++] = e;//0
//elementData[1++] = e;//1
elementData[size++] = e;
//最后返回给我们
return true;
}
ensureCapacityInternal方法
//代码顺序
//2 这里传递的是0和1
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//0==DEFAULTCAPACITY_EMPTY_ELEMENTDATA(默认值为0)
//条件成立
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//2.1这里他使用了一个数学计算类 Math.max传递两个值然后取最大值
//DEFAULT_CAPACITY默认是10,1比较大小,10比较大所以返回的是10
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
//如果上面条件不满足就返回1
return minCapacity;
}
//1,进入ensureCapacityInternal后他调用了calculateCapacity(elementData, minCapacity)这个方法然后进入2
private void ensureCapacityInternal(int minCapacity) {//参数传递1
//这里他将两个方法套在一起,我们先看
//calculateCapacity(elementData, minCapacity)//elementData默认值就是0,minCapacity传递参数为1
ensureExplicitCapacity( calculateCapacity(elementData, minCapacity) );
//2.1执行完成,那么就是ensureExplicitCapacity(10);
//接着执行3
}
//3
private void ensureExplicitCapacity(int minCapacity) {//10
//这里只是一个记录次数,跟我们add方法没关系不用管
modCount++;
// overflow-conscious code
//这里条件 10-0>0 条件成立
if (minCapacity - elementData.length > 0)
//执行grow(10)
grow(minCapacity);
}
//grow(minCapacity);10
private void grow(int minCapacity) {//传参10
//当前elementData是0,然后创建了一个新的变量oldCapacity赋值0
int oldCapacity = elementData.length;//0
//这里又创建了一个新的变量,0+(0>>1)翻译:>>是除以2 <<是乘以2 结果是0+(0/2)=0
int newCapacity = oldCapacity + (oldCapacity >> 1);//0
//这里判断0-10<0 = -10<0
if (newCapacity - minCapacity < 0)
//条件成立,将10赋值给newCapacity(0)
newCapacity = minCapacity;
//在判断10-MAX_ARRAY_SIZE<0 这里肯定是不成立的 MAX_ARRAY_SIZE取值范围是Integer的最大值-8 相当于10-(integer-8)>0
if (newCapacity - MAX_ARRAY_SIZE > 0)
//这里肯定不成立
newCapacity = hugeCapacity(minCapacity);
//最后执行了一个arrays.copyof复制 将0,10,传递进去
//返回的是一个数组,意思就算给数组指定大小,第一个参数表示给那个参数设置,数组大小取决于第二个参数
elementData = Arrays.copyOf(elementData, newCapacit);//10
//然后我们再回到第一层源码
}
Vector
跟list一样都是基于数组实现,但是他是线程安全的,效率没有arraylist高
他比arraylist多了一个枚举遍历
LinkedList
存储结构是:双向链表,从头指向尾,也可以从尾指向头
方法跟ArrayList一样
ArrayList跟LinkedList有什么区别?
总结
- ArrayList,vector,LinkedList都是实现的List接口
- ArrayList是基于数组实现的,有序,有下标,线程不安全,效率快,增删慢(因为数组是有序的,清除了某个下标位置那么后面的都需要往前移动)
- Vector也是基于数组实现的,有序,有下标,线程安全,效率慢
- LinkedList是基于双向链表实现的,查询效率慢,增删快。(查询需要顺着链表一个一个查询,增删只需要修改两个链表直接的联系)
泛型
泛型的基本使用
泛型类
Student
package 进阶.集合.泛型;
/**
* 创建泛型:
* 泛型可以写在类,变量,方法上 使用<T>表示
* @param <T>
*/
public class Student<T> {
private T name;
public void set(T name){
this.name=name;
}
public T get(){
return this.name;
}
}
Test
/**
* 使用泛型类创建一个对象
* 注意:
* 1.泛型只能使用引用类型,不同泛型中不能相互赋值
*/
public class Demo {
public static void main(String[] args) {
Student<String> stringStudent = new Student<>();
stringStudent.set("zhangsan");
System.out.println(stringStudent.get());
}
泛型接口
有两种可以实现,
一种是在impl中实现这个接口的时候就指定类型,
还有一种是在impl在上也加一个这样我们就可以在main方法中new这个对象的时候在指定
方式一:
day1
package 进阶.集合.泛型.impl;
/**
* 泛型接口
* 语法:接口名<T>
* 注意:不能泛型静态变量(常量)
*/
public interface day1<T> {
T test(T t);
}
day1impl
package 进阶.集合.泛型.impl;
public class day1impl implements day1<String>{
@Override
public String test(String s) {
return s;
}
}
test
package 进阶.集合.泛型.impl;
public class test {
public static void main(String[] args) {
day1impl day1impl = new day1impl();
System.out.println(day1impl.test("1111"));
}
}
方式二
day1
package 进阶.集合.泛型.impl;
/**
* 泛型接口
* 语法:接口名<T>
* 注意:不能泛型静态变量
*/
public interface day1<T> {
T test(T t);
}
day1impl
package 进阶.集合.泛型.impl;
public class day1impl<T> implements day1<T>{
@Override
public T test(T t) {
return t;
}
}
test
package 进阶.集合.泛型.impl;
public class test {
public static void main(String[] args) {
day1impl<Integer> integerday1impl = new day1impl<>();
Integer test = integerday1impl.test(1);
System.out.println(test);
}
}
泛型方法
package 进阶.集合.泛型.impl.泛型方法;
public class day1 {
/**
* 泛型方法
* 如果我们只想创建一个泛型方法不创建泛型类
* <T> 返回值类型
*/
public <T> T go(T t){
return t;
}
public static void main(String[] args) {
day1 day1 = new day1();
//这里返回类型是根据你传递的值来定义的
//如果传递的是String类型那么返回值就是String
String s = day1.go("S");
System.out.println(s);
}
}
泛型集合
为什么要设置集合泛型???
我们在使用集合的时候指定数据类型后,遍历后就不需要强转了
/**
* 泛型集合,
* 我们在创建集合的时候如果没有指定类型,那么默认就是Object的
* 那么我们在集合中即添加了Integer又添加了String类型
* 那么我们在遍历的时候我们就需要进行强制转换,那么如果我们强转成String那么我们Integer就会报错
* 所以我们也需要在集合中指定类型
*/
package 进阶.集合.泛型;
import java.util.ArrayList;
/**
* 泛型集合,
* 我们在创建集合的时候如果没有指定类型,那么默认就是Object的
* 那么我们在集合中即添加了Integer又添加了String类型
* 那么我们在遍历的时候我们就需要进行强制转换,那么如果我们强转成String那么我们Integer就会报错
* 所以我们也需要在集合中指定类型
*/
public class 泛型集合 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("a");
// list.add(1),因为我们已经指定了String类型,所以Integer就添加不上去了
for (String s :list) {
System.out.println(s);
}
}
}
Set集合
Set集合特点:无序,没有下标,不可重复元素
Set集合跟list集合一样,都是继承collection中的方法
简单使用
package 进阶.集合.collection.Set;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class Demo1 {
/**
* Set集合
* 特点:无序,无下标,不可以重复
*
* @param args
*/
public static void main(String[] args) {
Set<String> s=new HashSet<>();
//新增
System.out.println("==========新增============");
s.add("北京");
s.add("上海");
s.add("广州");
//当我们元素重复后,不能新增一样的,相当于覆盖
s.add("广州");
System.out.println(s.toString());
//清除
System.out.println("==========清除============");
//这里清除就不能使用下标清除了,只能使用元素清除元素
// s.remove("北京");
// System.out.println(s.toString());
//遍历
System.out.println("==========使用增强for循环遍历============");
//这里遍历就只能使用增强for循环了,因为没有下标不能使用for循环
for (String s1 : s) {
System.out.println(s1);
}
//使用迭代器
System.out.println("==========使用迭代器遍历============");
Iterator<String> iterator = s.iterator();
while (iterator.hasNext()){
String next = iterator.next();
System.out.println(next);
}
//判断
System.out.println("==========判断============");
System.out.println(s.isEmpty());//是否为空
System.out.println(s.contains("北京"));//判断是否有这个元素
}
}
HashSet
HashSet底层就是调用的HashMap的Key来保存的数据
1.7存储结构:(哈希表)数组+链表
1.8存储结构:(哈希表)数组+链表+红黑树
//HashSet添加
//是根据HashCode计算保存的位置,如果此位置为空那么就保存进去,如果不为空执行第二步,
//在执行equals方法,如果equals方法为true,则认为是重复,否者,形成链表。
package 进阶.集合.collection.Set.HashSet;
import java.util.HashSet;
public class Demo1 {
/*
* HashSet集合
* 存储结构:(哈希表)数组+链表+红黑树
* 存储方式:
1.根据hashcode,计算保存的位置(数组)如果当前此位置为空,则直接保存,如果不为空则执行第二步
2. 在执行equals方法,如果equals方法为true则认为是重复,否者形成链表
* */
public static void main(String[] args) {
//这里使用方法跟set集合一样
HashSet<String> set = new HashSet<>();
set.add("a");
set.add("b");
set.add("c");
System.out.println(set.toString());
}
}
存放对象
package 进阶.集合.collection.Set.HashSet;
import 进阶.集合.collection.Student;
import java.util.HashSet;
import java.util.LinkedList;
public class Demo1 {
/*
* HashSet集合
* 存储结构:数组+链表+红黑树
* */
public static void main(String[] args) {
//HashSet使用二
//存放对象
HashSet<Student> students = new HashSet<>();
Student student = new Student("xc",1);
Student student2 = new Student("xc",1);
Student student3 = new Student("xc",1);
students.add(student);
students.add(student);
students.add(student3);
System.out.println(students.toString());
}
}
如果student.add(new Student("xc",1))
这里我传递new一个对象填的参数跟student一样,为什么他不能去重复?
因为:add方法存放的是对象地址不是对象的值,所有去重不了
但是我们可以重写hashcode和equals
//重写hashcode是为了在添加时执行这个算法,判断当前的hashcode是否跟其他元素是否重复 //当前也是有可能两个不同对象计算的hashcode相同,这个时候就需要调用equals来跟两个对象做比较进一步确认是否相同 @Override public int hashCode() { //获取name字符串的hashcode int n1=this.name.hashCode(); //获取age int n2=this.age; return n1+n2; }
TreeSet
底层调用的是TreeMap,存储结构是红黑树
当我们想TreeSet中存放基本类型时,是可以正常存储的
但是当我们存储引用类型时,却发现报错
时因为,TreeSet是红黑树,需要跟书的根节点比较,如果比根节点小就分支到左边,如果比根节点大就放到右边
但是问题来了?
我们比较什么?两个对象我们怎么比较?是比姓名还是身高还是体重?
这个时候就需要实现下面的了。
//注意:
//在使用TreeSet存储对象的时候,必须要让对象实现implements Comparable<当前对象>,并且实现接口中的compareTo方法
//compareTo方法表示比较对象中的参数是否相同
//这个是判断TreeSet元素是否重复的
//如果此方法返回0就表示元素重复
//但是怎么比呢?我们先按照姓名比,如果姓名不一样就返回姓名的hashcode,如果一样就比较年龄,如果年龄一样就说明这个对象的姓名和年龄都一样是重复对象了,如果年龄不一样,就返回年龄
@Override
public int compareTo(Student o) {
//这里使用compareTo方法来比较两个String的值是否一样,如果一样就返回0
int n1=this.name.compareTo(o.getName());
//如果当前age-o.get==0就表示两个值一样
int n2=this.age-o.getAge();
//先按照姓名来比较如果相等就返回n2如果不相等就返回n1
return n1==0?n2:n1;
}
定制比较
Comparator
在我们上面使用的是元素比较会觉得很麻烦,如果我们多个TreeSet想实现不同的比较就么办法了。
这里我们使用定制比较,
我们在创建TreeSet的时候传递一个Comparator对象
然后我们就可以使用匿名内部类来做定制比较,这样我们在那个TreeSet集合中使用引用类型,就直接在当前类中定制好比较规则
package 进阶.集合.collection.Set.HashSet;
import 进阶.集合.collection.Student;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.TreeSet;
public class Demo1 {
/*
* TreeSet集合
* 存储结构:红黑树
* */
public static void main(String[] args) {
TreeSet<Student> tree = new TreeSet<>(new Comparator<Student>() {
//Comparator返回0表示相同,返回其他表示不相同
@Override
public int compare(Student o1, Student o2) {
//我们使用compareTo方法对象o1.name是否和o2.name一样,如果一样就返回0
int n1=o1.getName().compareTo(o2.getName());
//这里使用当前的年龄减去传递的年龄如果为0就表示一样的,不一样就直接返回
int n2=o1.getAge()-o2.getAge();
return n1==0?n2:n1;
}
});
Student student = new Student("aaa", 1);
Student student1 = new Student("asac", 1);
Student student2 = new Student("cdcd", 1);
tree.add(student);
tree.add(student1);
tree.add(student2);
System.out.println(tree.toString());
}
}
使用TreeSet实现字符串长度大小排序
package 进阶.集合.collection.Set.HashSet;
import 进阶.集合.collection.Student;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.TreeSet;
public class Demo1 {
/*
* TreeSet集合
* 存储结构:红黑树
* */
public static void main(String[] args) {
//使用TreeSet集合实现字符串长度从小到大排序
TreeSet<String> tree = new TreeSet<>(new Comparator<String>() {
//可以使用两个字符串比较长度就可以实现从大到小排序
@Override
public int compare(String o1, String o2) {
int n1=o1.length()-o2.length();
int n2=o1.compareTo(o2);
return n1==0?n2:n1;
}
});
tree.add("1");
tree.add("23");
tree.add("32342");
tree.add("4");
tree.add("53");
System.out.println(tree.toString());
}
}
Map集合
标记:重要方法,用于遍历
Map集合遍历原理
map里面存放了很多键值对,如果我们使用keyset方法就是将我们map中所有的key拿出来放到set集合中
如果我们使用entry()方法就是将我们所有的键值对拿出来放到entryset里面
entry比keyset效率高,因为keyset只是将key全部拿过去了,没有拿value,我们遍历的时候还需要根据key去map.get中获取value
但是entry是将map中所有的key和value都拿出了,可以直接将key和value直接循环遍历出来
HashMap(重点)
特点:线程不安全,执行效率快,允许使用null作为key或value
默认大小:16,加载因子75%的空null,加载因子指的是当前map大小为100,当达到75的时候就开始扩容
HashMap使用
package 进阶.集合.map;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class hashmap {
public static void main(String[] args) {
/**
* HashMap使用方法
* 存储结构:哈希表(数组+链表+红黑树)
* 存储方式:是根据hashcode计算存储的位置,如果为此位置为null就存进去,如果不为空,
* 就调用equals方法来比较两个对象是否相同,如果为true就表示相同,如果为false就表示不相同,就形成链表保存
*/
HashMap<Person,String> hashmap = new HashMap<Person,String>();
Person p1=new Person("孙悟空",18);
Person p2=new Person("猪八戒",19);
Person p3=new Person("沙和尚",20);
//添加
System.out.println("=======添加=======");
hashmap.put(p1,"北京");
hashmap.put(p2,"武汉");
hashmap.put(p3,"上海");
System.out.println("map元素个数:"+hashmap.size());
System.out.println(hashmap.toString());
//如果我使用这样添加,能不能添加进去?
//map不允许添加重复的key,应该添加不进去吧?
//实际上,是可以添加进去的,因为这里是创建了一个新的对象,跟p1是不同的两个对象,所以可以存放进去
hashmap.put(new Person("孙悟空",18),"北京");
//那我们如何实现这里不重复添加呢?
//我们重写Person对象中的hashcode和equals即可
System.out.println("=======清除=======");
//清除
// hashmap.remove(p1);
//使用这个也可以清除,因为重写了hashcode和equals
// hashmap.remove(new Person("沙和尚",20));
// System.out.println(hashmap.toString());
//遍历
System.out.println("====使用keyset方法遍历===");
Set<Person> people = hashmap.keySet();
for (Person p:people){
System.out.println(p+"=="+hashmap.get(p));
}
System.out.println("====使用entrySet()方法遍历===");
Set<Map.Entry<Person,String>> entry = hashmap.entrySet();
for (Map.Entry<Person,String> s :entry){
System.out.println(s.getKey()+"=="+s.getValue());
}
//判断
System.out.println(hashmap.isEmpty());//判断当前集合是否为空
System.out.println(hashmap.containsKey(p1));//判断当前key是否存在
System.out.println(hashmap.containsValue("北京"));//判断当前value是否存在
}
}
HashMap源码分析
源码总结:
- HashMap刚创建的时候,table是null,为了节省空间,当添加第一个元素是,table容量调整为16
- 当元素个数大于当前容量的(75%) (16*0.75=12)时,会进行扩容,扩容后的大小为原来的两倍16x2,目的是减少调整元素的个数
- jdk1.8 当每个链表长度大于8,且元素个小于64时,会调整为红黑树,目的是提高执行效率
- jdk1.8以前链表是从头插入,1.8以后从链表尾部插入
HashTable
存储方式:哈希表(数组+链表) 他不跟hashmap一样,一直在改变。key和value都不允许为null
创建时大小为0,添加一个元素的时候他的默认容量是11个,加载因子是0.75
TreeMap
存储方式:红黑树,还可以排序
如果传入基本类型是正常,如果我们传入引用类型就会跟TreeSet一样抛出类型转换异常
这是因为红黑树上的二叉需要将小的放到左边,大的放到右边,但是红黑树不知道怎么比较两个对象那个大那个小,
我们在引用类型上实现Comparable然后实现比较大小的方法
//比较红黑树中的二叉存放大小
@Override
public int compareTo(Person o) {
//因为id是唯一的,所以我们直接用id来比较大小即可
return this.Id;
}
然后我们定义好id后,我们每次存入的对象都是根据这个id来判断是否重复以及大小
这样我们就算TreeMap.put(new Person("xc",1),"北京"),他就更具id来判断是否重复,直接去重了
//比较二叉书大小
@Override
public int compareTo(Person o) {
//因为id是唯一的,所以我们直接用id来比较大小即可.
//用当前id-传递的id来获得大小
return this.Id-o.getId();
}
package 进阶.集合.map.Tree;
import 进阶.集合.map.Person;
import java.util.TreeMap;
public class Treemap {
public static void main(String[] args) {
/**
* TreeMap
* 存储结构:红黑树
*/
TreeMap<Person,String> treeMap = new TreeMap<>();
Person p1=new Person("孙悟空",18);
Person p2=new Person("猪八戒",19);
Person p3=new Person("沙和尚",20);
treeMap.put(p1,"北京");
treeMap.put(p2,"上海");
treeMap.put(p3,"天津");
treeMap.put(new Person("孙悟空",18),"北京");
System.out.println(treeMap.size());
System.out.println(treeMap.toString());
}
}