华清远见第三阶段Java高级技术总结

本文介绍了Java中的String类特性,包括字符串常量池、不可变性以及如何通过构造方法和拼接创建字符串。还讲解了StringBuilder和StringBuffer的区别,强调在多线程环境下线程安全的问题。此外,讨论了System类、Runtime类和Date类的相关方法。文章还涵盖了异常处理,包括异常分类和处理方式,以及集合框架中的ArrayList和LinkedList的区别。最后,提到了文件操作的File类和流的概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

String 字符串

String 是一个类,属于数据类型中的引用数据类型,java的一切使用” “引起来的内容,都是这个类的实例,称为字符串对象

字符串在定义后,值不可以改变,是一个常量,实际是一个字符数组,所以可以使用String.length();(长度)

String name="tom";//不存在tom,所以创建一个tom,将其地址保存在name变量中
name="jerry";//jerry在字符串常量池中不存在,就创建一个,将其地址保存在name变量中
name中保存的从tom的地址变为了jerry的地址,但是两个字符串对象依然存在
String name2="tom";//tom在字符串常量池中已经存在了,直接使用,将其地址保存在name2中
其实以上三句话实际只创建了两个字符串对象
System.out.println(name==name2);//输出false,因为name=jerry啦
每次使用定义字符串时,如果该字符串已经存在,直接使用存在的字符串对象,
如果不存在就创建一个新的字符串对象;
String类使用注意事项
  • 如果要频繁改动String类型变量的值,会创建很多字符串对象,效率很低,因此不要使用String类;

  • 如果要频繁改动字符串时,使用StringBuilder或者StringBuffer类

如何创建字符串对象
1.使用“ ”赋值形式创建
String str="sbc";
String str="ab";//当这句话执行时,会判断ab是否存在,没有就创建,将其地址保存在str中;

String str2="ab";//执行这句话时,判断ab是否存在,已存在,将其地址保存在str2中,

String str3="a"+"b";//这句话执行时,+两端如果都是“”定义的字符串,
//拼接后在判断“ab”是否存在于字符串缓冲区中,已存在将其地址保存到str3中
//以上三句话,只有一个字符串对象创建,即“ab”,str1,str2,str3指向了同一个地址,
//所以用==比较都是true
sout(str==str2);//输出为true;

sout(str==str3);//输出为true
2.通过构造方法创建

常用构造方法

说明

String()

创建一个空白字符串对象,即“ ”

String(String str)

创建一个指定字符串对象

String(char[] list)

创建一个指定字符数组的字符串对象

String(byte[] bytes,String charsetName)

按指定的编码格式创建一个指定字节数组的字符串对象

2.1使用构造方法String(String str)创建
//这句话执行流程:
//1、在字符串缓冲区中寻找ab,不存在,在缓冲区创建
//2、在堆中new String()创建对象将字符串缓冲区中的ab的字符串地址保存在new String()区域中
//3、将堆中new String()整个对象保存到栈中Str1变量中
String str1=new String("ab");
//这句话执行流程:
//1、在缓冲区中寻找ab,存在
//2、在堆中new String()创建对象将字符串缓冲区中的ab的字符串地址保存在new String()区域中
//3、将堆中new String()整个对象保存到栈中Str2变量中
String str2=new String("ab");


//以上两句话,在字符串缓冲区中有一个ab的字符串,在堆中有两个对象,
//str1和str2保存堆中不同的两个地址,所以输出weifalse
System.out.println(str1==str2);//false
3.使用+拼接“”和new出来的字符串对象创建
String str1="ab";//在字符串缓冲区中创建“ab";
//1、创建StringBuilder对象
//2、在字符串缓冲区中创建”a”;在字符串缓冲区中创建”b”;
//3、在堆中new String(),将“b”保存在其中
//4、调用StringBuilderd对象的append()方法,将“a”和new String("b")拼接
String str2="a"+new String("b");//一共会创建"a","b",new String(),
//new StringBuilder()四个对象
System.out.println(str1==str2);//false,比较两个不同地址
String类总结

在使用字符串时,字符串是对象,如果要比较两个值是否相同,不能使用==判断,因为==判断的是内存地址,所以在比较字符串是否相等时,要使用String类重写的equals方法进行判断

重写equals方法的原理:

判断是否为同一个字符串,再判断是否是字符串类型,再将两个字符串转换为字节数组,逐一比较字节数组中的内容,全部一致,返回为true;

字符串相关面试题

//题目一
String str1="ab";
String str2="ab";
String str3="a"+"b";
String str4=new String("ab");//使用字符串缓冲区中已经生成的ab,将其保存在new String()中
//创建"a"和"b",将”b“保存在new String(),将”a“和new String()保存在StringBuilder()对象中
String str5="a"+new String("b");
System.out.println(str1==str2);//true
System.out.println(str1==str3);//true
System.out.println(str1==str4);//false
System.out.println(str1==str5);//false
//题目二
String s1="abc";
String s2="a"+"b"+"c";
问:以上两句话执行后会创建几个对象?
答:一个,"abc"



//题目三
String str=new String("Hello");
问:这句话执行时会创建几个对象?
答:一个或者两个,
如果字符串缓冲区有"hello",只会创建new String(),这时只创建一个对象
如果字符串缓冲区没有"hello",会创建"hello"和new String(),这时会创建两个对象



//题目四
String s3=new String("wor");堆中new String(),和常量池”wor”
String s4=s3+"ld"; 堆中new StringBuilder()和常量池“ld”
以上两句话执行会创建几个对象?
四个
+号两都是“”赋值的字符串,拼接发生在编译阶段,将最终拼接的结果保存在字符串缓冲区中
其余情况都会创建StringBulider拼接字符串

字符串String类中的常用方法

方法名

返回值

作用

length()

int

得到字符串长度

toUpperCase()

String

转换为大写

toLowerCase()

String

转换为小写

trim()

String

去掉字符串的首尾全部空格

isEmpty()

boolean

判断字符串长度是否为0

getBytes()

byte[]

转换为字节数组

toCharArray()

char[]

转换为字符数组

equalslgnoreCase(String str)

boolean

忽略大小写比较字符串是否相同

equals(String str)

boolean

判断两个字符串是否相同

charAt(int index)

char

得到某个索引下标上的字符

indexOf(String str)

int

得到某个字符串第一次出现的索引,不存在返回-1

lastIndexOf(String str)

int

得到某个字符串最后一次出现的索引。不存在返回-1

contains(String str)

boolean

判断是否包含指定的某个字符串

startsWith(String str)

boolean

判断是否是以指定字符串开头

endsWith(String str)

boolean

判断是否是以指定字符串结尾

concat(String str)

String

将指定字符串拼接到原字符串末尾

substring(int index)

String

从索引指定下标开始截取字符串至末尾

substring(int begin,int end)

String

截取[begin,end)范围内的字符串

split(String regex)

String[]

根据字符串或正则表达式切割原字符串

replace(String oldStr,String newStr)

String

将原字符串中的oldStr替换为newStr

String.valueOf(参数)

String

将参数转换为字符串,参数可以是任何数据,通常用于原始类型转换为字符串

String.format(String 格式,Object...obj)

String

根据指定格式转换参数,常用于将浮点数据保留指定小数位数,如String.format("%4.2f",2.345)表示将2.345保留2位小数,整体占4位,输出字符串格式,如果实际数字总为数大于4,原样输出,如果实际数字总位数小于4,会在最前补充空格

可变字符串StringBuilder、StringBuffer

String字符串对象是一个常量,在定义后,值不可以改变,如果使用String类的对象,对其频繁更新是,就会不停的创建新对象,不停引用给同一个变量;

例如:

如果要执行10000次循环重新赋值的过程,就会创建10000个字符串对象,每个对象的创建都需要时间和空间,循环时实际上不是更新字符串,而是创建字符串,效率很低,这就是需要使用可变字符串对象

StringBuilder

用于表示可变字符串的一个类,是非线程安全的,在单线程环境下使用,效率更高

StringBuffer

用于表示可变字符串的一个类,是线程安全的,在多线程环境下使用

StringBuilder和StringBuffer中的方法都一致,只不过StringBuffer中的方法使用了synchronized关键字修饰,表示是一个同步方法,在多线程环境下不会出现问题。

例如:PersonA类

public class PersonA implements Runnable{
    private Object knife;
    private Object fork;
    private Object paper;

    public PersonA(Object knife, Object fork,Object paper) {
        this.knife = knife;
        this.fork = fork;
        this.paper=paper;
    }

    @Override
    public void run() {
        synchronized (paper){
            synchronized(knife){
                System.out.println(Thread.currentThread().getName()+"获得了刀,2秒后获得叉");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized (fork){
                    System.out.println(Thread.currentThread().getName()+"已获得了叉和刀,可以开饭了");
                }
            }
        }
    }

PersonB类

public class PersonB implements Runnable {
    private Object knife;
    private Object fork;
    private Object paper;

    public PersonB(Object knife, Object fork,Object paper) {
        this.knife = knife;
        this.fork = fork;
        this.paper=paper;
    }

    @Override
    public void run() {
        synchronized (paper){
            synchronized (fork){
                System.out.println(Thread.currentThread().getName()+"获得了叉,2秒后获得刀");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized (knife){
                    System.out.println(Thread.currentThread().getName()+"已经获得刀和叉,开饭了");
                }
            }
        }

    }

Main类

 public static void main(String[] args) {
        Object knife=new Object();
        Object fork=new Object();
        Object paper=new Object();
        PersonA personA=new PersonA(paper,knife,fork);
        PersonB personB=new PersonB(fork,knife,paper);
        Thread thread1=new Thread(personA,"小张");
        Thread thread2=new Thread(personB,"老王");
        thread1.start();
        thread2.start();
    }
构造方法

StringBuilder、StringBuffer的构造方法都一样

常用构造方法

作用

StringBuilder()

创建一个大小为16的字节数组,表示一个空白字符串

StringBuilder(int capacity)

创建一个指定大小的字节数组,表示一个空白字符串

StringBuilder(String str)

创建一个str的长度+16大小的字节数组,表示str这个字符串

常用方法

常用方法

作用

append(Object obj)

将任意数据添加到原可变字符串末尾

delete(int start,int end)

删除从[start,end)范围内的字符

deleteCharAt(int index)

删除指定下标索引上的字符

insert(int index,Object obj)

将obj添加到index指定下标上

replace(int start,int end,String str)

将[start,end)范围内的字符替换成str

reverse()

反转字符串

注意
  • String类中所有的方法调用后,都会创建一个新的String对象,及原本的String字符串不会改变;

  • StringBuilder类中所有的方法都是在操作同一个字符串对象,每次调用方法,都会让原字符串发生改变;

  • StringBuilder类中没有重写equals的方法,所以判断两个可变字符串对象是否相同时,如果调用equals方法,实际调用的是Object中未重写的方法,即==判断,所以判断可变字符串是否相同时,需要将其转换为String对象再调用equals方法

可变字符串与String之间的转换

String转换为可变字符串
String str="hello";
//使用构造方法将String对象转换为StringBuilder对象
StringBuilder sb=new StringBuilder (str);
可变字符串转换为String(任意类型对象转换为String)
  • String.valueOf(Object obj)方法

StringBuilder sb=new StringBuilder ("hello");
//将任意类型对象转换为String对象
String str=String.valueOf(sb);
  • toString()方法

StringBuilder sb=new StringBuilder("hello");
//调用任意对象的toString()方法
String str=sb.toString();
  • 拼接字符串

StringBulider sb=new StringBuilder("hello");
String str=sb+"";

可变字符串相关面试题

比较String,StringBuilder,StringBuffer的区别:

相同点:

  • 这三个类都可以表示字符串,都提供了一些操作字符串的方法

  • 这三个类中有相同的方法,如char(),indexOf()等

  • 这三个类都是被final修饰的类,不能被继承

不同点:

  • String定义的字符串是一个常量,可变字符串定义的字符串是一个变量

  • String类中的方法调用后,不会改变原本字符串的值,可变字符串中的方法调用后,会改变原本字符串的值

  • StringBuilder是非线程安全的可变字符串,StringBuffer是线程安全的可变字符串,其中的方法被synchronized修饰

System类

这个类中包含了一些系统相关的信息和一些方法,其中的属性和方法都是静态的,该类不能创建对象,不是因为它是抽象类,而是因为它的构造方法是私有的。

常用方法和属性

常用方法和属性

说明

System.out

获取标准输出流对象,用于打印信息

System.in

获取标准输入流对象,获取输入的信息

System.err

获取错误输出流对象,用于打印异常信息

System.exit(int statues)

终止虚拟机运行,参数0表示正常终止

System.currentTimeMills()

获取从1970/1/1 8:0:0至今经过了多少毫秒,返回值为long类型,通常称为时间戳

System.arraycopy(原数组,原数组的起始位置,目标数组,目标数组的起始位置,要复制的元素数量)

将原数组中指定数量的元素复制到新数组中

Runtime类

Runtime类的对象,表示程序运行时对象(程序运行时环境对象)包含了程序运行环境相关的信息,常用语获取运行环境信息(如虚拟机信息)或执行某个命令

特点

这个类不是一个抽象类,但不能创建对象,因为它的构造方法是私有的,这个类提供了一个静态方法getRuntime(),通过该方法,可以获取一个Runtiem类的对象

这种方式可以保证该类只能创建一个对象,是java中的一种设计模式,:单例模式

public class Runtime{
    //定义一个私有的静态成员,创建了一个当前类的对象
    private static Runtime currentRuntime =new Runtime();
    //将构造方法私有,无法在外创建对象
    private Runtime();
    //定义了一个公开的静态方法,用于获取创建的唯一的当前类的对象
    public static Runtime getRuntime(){
        return currentRuntime ;
    }
}
Runtime方法的使用

使用Runtime类中的方法,先通过其静态方法获取他的对象

public class Test{
    public static void main(String[]args)throws IOException,InterruptedException{
        //使用Runtime类中的方法,先通过其静态方法获取他的对象   
        Runtime runtime=Runtime.getRuntime();
        System.out.println("当前虚拟机的空闲内容"+runtime.totalMemory()/1024/1024+"MB");
        System.out.println("当前虚拟机的实际最大内存"+runtime.freeMenory()/1024/1024+"MB");
        System.out.println("当前虚拟机支持的最大内存"+runtime.maxMemory()/1024/1024+"MB");

        //exec(String 指令名) 运行某个指令,返回运行的进程对象
        //mspaint画图 calc计算器 notepad记事本
        Process mspaint =runtime.exec("mspaint");//打开画图
        Thread.sleep(3000);//休眠3秒

    }
}

Date类

date日期 用于表示日期时间的类,位于java.util包下

构造方法

常用构造方法

说明

Date()

创建当前时间对应的日期对象

Date(long l)

创建指定瞬间对应的日期对象

Date(int year,int month,int date)

根据年月日创建日期对象

常用方法

常用方法

作用

getTime()

得到Date对应对象的毫秒数

after(Date when)

判断参数是否在调用日期之后

before(Date when)

判断参数是否在调用日期之前

SimpleDateFormat类

用于格式化日期的类

构造方法

常用构造方法

作用

SimpleDateFormat(String pattern)

创建一个指定日期模板的格式化日期对象

日期模板

yyyy:年 MM:月 dd:日 hh:12小时制 HH:24小时制 mm:分 ss:秒 E:星期

如:yyyy/MM/dd/ HH:mm:ss E表示2023/03/09 14:05:16 周四

常用方法

常用方法

返回值

作用

format(Date)

String

将Date对象按日期模板转换为字符串

parse(String str)

Date

将满足日期模板的字符串转换为Date对象

格式
根据指定模板创建格式化日期对象
public class DateText {
    public static void main(String[]args){
        Date date=new Date();
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy/MM/dd HH:mm:ss E");
        //将满足日期模板的字符串转换为Date对象
        String format=sdf.format(date);
        System.out.println(format);
    }
}

Calendar类

表示日历的类,包含了很多与日历相关的信息,Calendar是一个抽象类,无法创建对象,可以通过静态方法**getInstrance()**获取Calendar类的实例

//获取Calendar类的实例
Calendar c=Calendar.getInstrance();
日历字段

在Calendar类中,定义了很多被final static修饰的静态常量,称为日历字段,实际是一个数字,用于获取指定日历信息

Calendar常用方法

常用方法

作用

get(int field)

根据日历字段获取相应的值

getMaximum(int field)

获取指定日历字段的最大值,如日期最大为31

getActualIMaximum(int field)

获取指定日历段的实际最大值,如11月的日期最大为30

getTime()

将Calendar对象转换为Date对象

set(int year,int month,int date)

同时设置日历的年月日

setTime(Date date)

将Date对象作为参数设置日历的信息

set(int field,int value)

将指定的日历段设置为指定值

方法调用时传值问题

第一种:参数是原始类型,方法内部对参数重新赋值,实参没有影响,main方法中是实际参数
public class Main{
    public static void fun1(int i){
        i=123;
        System.out.println(i);
    }

    Main主函数
    public static void main(String[]args){
         int i=0;//实参
         fun1(i);//输出为123,
         System.out.println(i); //输出为0
    }
}
第二种:参数是String,方法内部对参数重新赋值,实参没有影响
public static void fun2(String str){
    str="new str";
    System.out.println(str);
}
public static void main(String[]args){
    String oldstr="old str";
    fun2(oldstr);//输出"new str"
    System.out.println(oldstr);输出"old str"
}
第三种:参数引用类型,方法内部在操作参数,实参受影响
public static void fun3(Person p){
    p.setName("吴彦祖");
    System.out.println(p.getName);
}
public static void main(String[]args){
    Person p=new Person();
    p.setName("小王");
    fun3(p);//输出吴彦祖
    System.out.println(p.getName());输出吴彦祖
}
第四种:参数是引用类型,方法内部对形参重新赋值后操作,实参没有影响

pblic static void fun4(Person p){
    p=new Penson();    
    p.setName("胡歌");
    System.out.println(p.getName());
}
public static void main(String[]args){
    Person p=new Penson();
    p.setName("小李");
    fun4(p);//输出胡歌
    System.out.println(p.getName());//输出小李
}
第五种:参数是引用类型,方法内部在操作参数,实参受影响
public static void fun5(int[] nums){
    nums[0]=123;
    System.out.println(dnum[0]);
}
public static void main(String[]args){
    int[] nums={1,2,3,4};
    fun5(nums);//输出123
    System.out.println(nums[0]);//输入123
}

例题:

方法体
public static void fun5(char[] list,Person p){
   list[0]='m';//方法体内部直接操作实际参数,会影响主函数中的实际参数
    p=new Person();//方法内部创建了新对象给实际参数重新赋值,不会影响主函数的实际参数
    p.setName("xxx"); 
}
main主函数
public static void main(String[]args){
    Person p=new Person();
    p.setName("qwe");//实参
    char[] list={'a','b’,'c',''d};//实参
    fun5(list,p);
    System.out.println(p.getName());//输出qwe
    System.out.println(list[0]);//输出m
}

包装类

java是纯面向对象语言,宗旨是将一切事物视为对象处理

包装类是原始类对应的类类型

特点
  • 八个原始类型中,除了int和char,其余包装类都是将原始类型的首字母改为大写,int对应Integer,char对应Character

  • 包装类都是被final修饰的,不能被继承

  • 除了Character类,其余包装类都有两个过时的构造方法,参数为对应的原始类型或者字符串Character只有一个参数为char类型的构造方法,构造方法的目的都是将原始类型的数据转换为包装类的对象

  • 除了Character类,其余包装类都有静态方法parse原始类型单词(String str),用于将字符串换为相应的原始类型

  • 数值型的包装类parseXXX()方法,如果参数不是对应的数字,就会抛出NumberFormat异常

  • boolean的包装类Boolean的parseBoolean()方法,如果参数不是true这个单词,运行结果都是false

  • 除了Boolean类,其余包装类都有MAX_VALUE和MIN_VALUE这两个静态属性,用于获取对应类支持的最大最小值

  • 所有包装类都重写了toString(),用于将包装类对象转换为String对象

字符串与原始类型之间的转换

字符串转换为原始类型

使用原始类型对应的包装类,调用parseXXX(String str)方法

String num="123";
byte b==Byte.parseByte(num);//输出123
float l=Folat.parseFloat(num);//123.0
boolean b1=Boolean.parseBoolean(num);//false
原始类型转换为字符串
  • 使用String类的String.valueOf(Object obj)

int num=123;
String str=String.valueOf(num);//输出字符串123
  • 拼接空白字符串

int num=123;
String str=num+"";//输出字符串123
  • 将原始类型转换为包装类后调用toString()

int num=123;
Integer integer=new Integer(num);
String str=integer.toString();//输出字符串123

装箱和拆箱

  • 自动装箱缓冲区

i1和i2保存的数字在byte范围[-127,127]内,这个值会共享,只会有一个“100”的对象
Integer i1=100;
Integer i2=100;
System,out.println(i1==i2);//true,i1和i2引用一个地址

Integer i3=128;
Integer i4=128;
System,out.println(i1==i2);false
System,out.println(i1.equals(i2));//true,包装类重写了equals,会拆箱后判断
  • 使用自动装箱给包装类对象赋值,值得范围在byte范围[-127,127]内,这个值会保存在缓冲区中,如果多个对象都使用这个值,共享一个数据,使用同一个地址,==判断结果为true;值得范围不在byte范围内,就会创建新的包装类对象,会有不同的地址,==判断结果为false;

  • 引用类型对象比较相同时,不要使用==,包括包装类对象,比较相同时,使用包装类重写的equals方法

异常

当程序没有按照开发人员的意愿正常执行,中途出现的错误导致程序中断,称为异常

异常的产生

异常在程序中以对对象的形式存在,当代码执行过程中出现异常后,虚拟机会自动创建一个异常对象,如果没有对该异常对象进行处理,就会导致程序中断,不在执行后续内容,

异常的分类
Error错误

如果出现XXXError,如stackOverFlowError,栈溢出,无法通过额外的代码解决,只能修改源代码

Exception异常
  • RunTimeException运行时异常

  • 如果一个异常类属于RuntiemExceotion异常类的子类,称这个异常为运行时异常,可以通过编译,运行时可以抛出异常对象

常见运行时异常

说明

出现的情景

NullPointException

空指针异常

如用空对象null调用属性或者方法

IndexOutOfBoundsException

索引越界异常

如当使用某个带有索引的对象超出范围

NumberFormatException

数字格式异常

如调用包装类的parseXXX()方法,如果参数不能转换

InputMismatchException

输入不匹配异常

如使用Scanner接受控制台输入时,如果输入的数据不是对应的类型

ClassCastException

对象转换异常

如Person p=(Person )Dog dog

ArithmeticException

算术运算异常

如0当分母编译时异常

  • 编译时异常

如果一个异常类不属于RunTimeException异常类的子类,称这个异常为编译时异常,无法通过编译,必须要处理异常后才能编译运行

常见编译时异常

说明

出现的情景

IOException

输入输出流异常

使用流对象

FileNotFoundException

文件未找到

方法的参数为文件对象时

SQLException

数据库相关异常

操作数据库时出现

处理异常

通常所说的处理异常,指的是处理Exception类的子类异常

方式一:try-catch-finally语句

这种方式处理异常,无论会不会抛出异常,都能让程序正常执行

try{
    //可能出错的代码
}catch(异常类 异常对象){
    //如果try中的代码抛出异常,异常对象属于catch中的异常类型,就会执行这里的代码
}finally{
//无论程序是否出抛出异常,都要执行这里的代码
}
try-catch-finally使用时注意
  • 如果代码会抛出多个异常,可以使用多个catch进行捕获,需要将子类异常放在最前面,父类异常放在最后面

  • try,catch,finally都不能单独使用,try必须配合catch或者finally一起使用

  • 无论try中的代码是否会抛出异常,finally中的代码都会执行

  • 执行try中的代码是,如果出现异常,就不再执行try中剩余代码。try中定义的内容,无法再try之外的地方使用

  • try中如果有return,不影响finally的执行,finally优先于return执行

方式二:throws关键字

该方法可以让编译时异常通过编译

在定义方法的时候,通过该关键字声明方法可能抛出的异常

用法:方法的参数部分后,添加throws异常类型1,异常类型2....

public class Test{
    public void fun() throws InterruptedException{
        thread.sleep(500);//该方法就会有一个声明:可能会抛出InterruptedException异常
sleep()方法在源码中声明了可能会抛出InterruptedException异常
InterruptedException异常不是RunTimeException的子类异常,必须要处理才能通过编译,要么使用try-catch,要么继续声明异常

    }
}

throw和throws

  • throws表示用于声明方法可能出现的异常,使用时卸载方法的小括号之后

public void fun() throws InterruptedException{
  thread.sleep(500);
}
  • throw用于手动抛出异常对象,使用时,卸载方法体中,“throw异常对象”。常用于满足某种条件时,强制中断程序

public void fun(){
    throw;
}

自定义异常

如果需要在某种情况下中断程序,可以自定义一个异常类,再通过throw关键字手动抛出自定义异常类

自定义异常步骤

1、创建一个类,继承某个异常类

  • 如果继承的是RunTimeException,表示自定义的异常类属于运行时异常,该异常对象可以不用处理

  • 如果继承的是非RunTimeException,表示自定义的异常类属于编译时异常,该异常对象必须要处理

2、可选操作,定义带参构造方法,参数为String类型的异常信息,调用父类中的构造方法

/*
*自定义异常
只需要继承某个异常类即可
是否定义构造方法根据实际情况决定
*/
public class MyException extends NullpointerException{
    //带参构造,参数为异常信息
    public MyException(String msg){
        super(msg);
    }
//无参构造
    public MyException(){
        super();
    }
}

数组和集合

数组的特点
  • 数组中保存的元素都是有序的,可以通过索引快速访问

  • 数组中保存的元素都是同一种类型

  • 数组的长度在定义后,无法改变

  • 数组无法获取其中保存的元素实际数量

集合的特点
  • 能保存一组数据,元素可以有序或者无序(存入的顺序和读取的顺序不一致)

  • 集合中保存的元素的数据类型可以不同

  • 集合的容量可变

  • 可以获取集合中保存的元素实际数量

集合框架

图上的所有实现类,都是非线程安全的,在多线程环境下使用以上任意集合,都会出现数据不准确的情况。

Collection接口

该接口中有两个核心子接口:List和Set

这两个接口都可以保存一组元素,List接口保存元素有序可重复的,Set接口保存元素无序不重复

Collection接口有一个父接口iterable,它不是一个集合,而是用于遍历集合的工具接口,包含forEach()和iterator()方法

常用方法

返回值

作用

add(Object obj)

boolean

将元素添加到集合中

size()

int

获取集合中的元素数量

isEmpty()

boolean

判断集合是否为空

clear()

void

清空集合

contains(Object obj)

boolean

判断集合中是否包含指定元素

remove(Object obj)

boolean

移除集合中的指定元素

toArray()

Object[]

将集合转换为数组

stream()

Stream

获取集合的流对象,用于遍历集合

iterator()

Iterator

得到集合的迭代器对象,用于遍历集合

List接口(有序可重复)

有序集合,元素可以重复,允许保存null,可以通过索引获取对应的元素

List接口在继承Collention接口后,有拓展了一些操作元素的方法

拓展方法

返回值

作用

get(int index)

Object

得到指定索引的元素

set(int index,Object obj)

Object

使用obj替换index下标上的元素,返回被替换的元素

add(int index,Object obj)

void

将obj添加到index上

remove(int index)

Object

移除指定索引的元素,返回被移除的元素

indexOf(Object obj)

int

得到obj第一次出现的索引

lastIndexOf(Objcet obj)

int

得到obj最后一次出现的索引

subList(int form,int to)

List

截取[from,to)区间内的元素,返回子集合

构造方法

构造方法

说明

ArrayList()

创建一个Object类型的空数组,在调后续添加方法时,才会初始化数组大小

ArrayList(int initilaCapacity)

创建一个指定容量的Object类型的数组,如果参数为负数,会抛出IllegalArgumentException异常

ArrayList(Collection c)

根据指定集合创建Object类型数组

ArrayList和LinkedList的区别

  • 这两个类都是List接口的实现类,保存的元素有序可重复,允许保存null

  • ArrayList采用数组实现,随机读取效率高,插入和删除效率低,是用于查询

  • LinkedList采用双向链表实现,插入和删除效率高,随机读取效率低,适用于频繁更新集合

遍历集合的方式

遍历List集合

  • 普通for集合

for(int i=0;i<集合.size();i++){
    元素 变量=集合.get(i);
}
  • 增强for循环

for(数据类型 变量名:集合){
元素 变量=集合.get(i);
}
  • forEach()方法

集合.forEach(obj->{
    元素 变量=集合.get(i);
});
  • 迭代器

//Collection接口有一个父接口Iterable,其中有一个iterator方法用于获取迭代器对象遍历集合
//所有Collection的子实现类都能调用该方法
Iterator it = Collection集合.iterator();
//hasNext()判断是否还有下一个元素
while(it.hasNext()){
//next()方法读取该元素
元素 变量 = it.next();
}
遍历HashMap集合
泛型

一种规范,常用于限制集合中的元素类型,省去遍历集合时转换Object对象的过程

//默认可以保存任意类型的元素,即Object类型
List list=new ArrayList();
list.add(123);
list.add("hello");
//遍历时只能使用object类型获取
for(Object obj:list){

}
使用泛型

在定义集合时,在集合或者接口后写上<引用数据类型>

例如: 集合类或者接口<引用数据类型>集合遍历名=new 集合实现类();

//定义只能保存整数的集合,要使用整数的包装类类型
List <Integer> list=new ArrayList();
list.add(123);
//不能添加非整数
 如list.add("hello");

Arrays数组工具类

包含了一些操作数组的静态方法

常用静态方法

说明

Arrays.Sort()

对数组中的元素升序排序

Arrays.asList(T...obj)

将可变参数转换为ArrayList集合

集合和数组之间的转化

数组转换为集合
//调用Arrays工具类的asList(1,2,6,22,11)或者asList(数组)
ArrayList<Integer>list=Arrays.asList(1,2,6,22,11);
集合转换为数组
ArrayList list=new ArrayList();
list.add("sdf");
list.add(123);
list.add(null);
//调用集合的toArray()方法
Object[] objs=list.toArray();

无论是数组转换集合还是集合转换数组,都可以进行遍历。

如果集合转换为数组,遍历集合,通过索引赋值。

如果数组转换为集合,遍历数组,通过add()添加元素

文件类File

java中的File类,表示本地硬盘中file或文件夹directory的一个类

构造方法

常用构造方法

说明

File(String pathName)

根据文件的完整路径创建File对象

File(String parent,String child)

根据文件的父目录的路径和自身的名称创建File对象

File(File parent,String child)

根据文件的父目录对应的File对象和自身的名称创建File对象

常用方法

常用方法

作用

exists()

判断文件是否存在

isFile()

判断是否为文件

isDirectory()

判断是否为文件夹

getName()

获取文件名

getPath()

获取文件相对路径

getAbsolutePath()

获取文件绝对路径

length()

获取文件字节大小

listFiles()

得到文件夹中的第一层子文件对象的数组,返回file[]

delete()

删除文件或者空文件夹

mkdir()

创建文件夹

斐波那契数列
public class Test2 {  
    public static void main(String[] args) {
//兔子问题
//有一对兔子,在第三个月开始,每个月都生一公一母两只小兔子
//假设所有兔子都不死亡,第10个月一共有多少只
//1月 2月 3月 4月 5月 6月 7月 8月 9月 10月
//1 1 2 3 5 8 13 21 34 55
//斐波那契数列
        System.out.println(fun(3));
        System.out.println(fun(10));
    }
/*
* 递归调用
* */
    public static int fun(int n) {
        if (n > 2) {
        return fun(n - 1) + fun(n - 2);
        }
        return 1;
    }
}
递归遍历文件夹
public class Test3 {
    public static void main(String[] args) {
    //输出某个文件夹下的所有文件
        File source = new File("D:\\GamePP Wonderful Moment");
        showAllFile(source);
    }
    public static void showAllFile(File source){
    //判断如果是文件夹
        if(source.isDirectory()){
        //展开第一层
            for (File child : source.listFiles()) {
                //child就是第一层的所有子文件,有可能还是文件夹,递归调用本方法
                showAllFile(child);
            }
        }
        //输出名称
        System.out.println(source.getName());
    }
}

流Stream

在Java中,流表示计算机硬盘与内存之间传输数据的通道

将内存中的数据存入到硬盘中,称为写write,也称为输出Output

将硬盘中的数据存入到内存中,称为读read,也称为输入Input

流的分类

字节输入流InputStream

FileInputStream、ObjectInputStream

字节输出流OutputStream

FileOutputStream、ObjectOutputStream

字符输入流Reader

FileReader、BufferedReader、InputStreamReader

字符输出流Writer

FileWriter、BufferedWriter、OutputStreamWriter

按方向分类

  • 输入流:InputStream、Reader

  • 读硬盘中的数据到程序中

  • 输出流:OutputStream、Writer

  • 将程序中的数据写到硬盘中

按数据传输类型分类

  • 字节流:InputStream、OutputStream

  • 读写非文本类型文件。如图片、多媒体文件

  • 字符流:Reader、Writer

  • 读写纯文本文件。如txt、md等

流的四个父类的特点

  • 这四个父类都是在java.io包下,都是抽象类,不能直接创建其对象,使用其子类对象

  • 这四个类都有close()方法,用于关闭流对象,释放资源

  • 输入流(InputStream和Reader)都有read()方法,用于读取数据,输出流(OutputStream和Writer)都有write()方法

  • 输出流(OutputStream和Writer)都有flush()方法,用于将流中的数据冲刷到硬盘中,在使用输出流对象时,一定要调用flush()或close()方法后,才能真正将数据写入到硬盘中

  • 所有流中,以Stream结尾,都是字节流,数据以字节传输;以Reader或Writer结尾,都是字符流,数据以字符传输

  • 读取硬盘中的数据时,读取的文件必须存在;写入数据到硬盘中时,写入的文件可以不存在,但父

目录必须存在。

FileInputStream文件字节输入流(掌握)

构造方法
常用方法

FileOutputStream文件字节输出流(掌握)

构造方法
常用方法

FileReader文件字符输入流

构造方法
常用方法

BufferedReader缓冲字符输入流(掌握)

构造方法
常用方法

FileWriter文件字符输出流

构造方法
常用方法

BufferedWriter缓冲字符输出流(掌握)

必须在调用flush()或close()方法后才会写入。

构造方法
常用方法

序列化ObjectOutputStream对象字节输出流(掌握)

序列化:将对象转换为文件的过程

被序列化的对象,其类必须要实现Serializable接口

这个接口是一个特殊的接口,其中没有定义任何方法,只是给类加上标记,表示该类可以被序列化

构造方法
常用方法

反序列化ObjectInputStream对象字节输入流(掌握)

反序列化:将文件转换为对象的过程

构造方法
常用方法

进程和线程

进程Process

进程就是操作系统中正在执行的程序。

一个程序就是一个执行的进程实体对象。

每个运行中的进程,都有属于它独立的内存空间,各个进程之间互不影响。

线程Thread

线程是一个进程中的执行单元,一个进程中可以有多个线程。

多个线程之间可以访问同一个进程中的资源。

每个线程都有一块独立的栈空间,这些线程所在的栈空间位于同一个进程空间中。

多线程

如果一个进程中,同时执行着多个线程,就称为多线程。

多线程可以提高程序执行效率。如多个窗口卖票,就可以加快卖票的效率。

其实每个执行的Java程序,都是多线程执行。main方法所在的线程称为主线程,还有gc线程(守护线程)

在同时运行。

实现多线程

方式一:继承Thread类
方式二:实现Runnable接口
方式三:使用匿名内部类

多线程相关面试题

  • 为什么说StringBuilder或ArrayList、HashMap是非线程安全的?

public class Test {
    public static void main(String[] args) throws InterruptedException {
//使用StringBuilder时,很大概率最终数值不准确,非线程安全
//StringBuilder sb = new StringBuilder();
//使用StringBuffer时,没有问题,线程安全
        StringBuffer sb = new StringBuffer();
//创建10个线程对象,创建后进入就绪状态
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    Thread.sleep(1);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                        }
//每个线程对象添加字符串
                        for (int j = 0; j < 10; j++) {
                               sb.append("hello");
                        }
                        }).start();
                    }
                    Thread.sleep(5000);
//如果没有任何问题,每个线程拼接50个字母,10个线程拼接500个,sb的长度为500
                    System.out.println(sb.length());
            }
}
  • 解决方法

方式一:让两个线程获取资源的顺序保持一致,如两个线程都先获取knife,再获取fork

方式二:在两个线程获取的资源A和B之前,再获取第三个资源C,对这个资源C使用synchronized进行同步这样在某个线程获取资源C后,继续执行后续的内容,知道执行完毕,其他线程才有机会开始执行。如在获取knife和fork之前,先获取paper对象

总结

多写多练 好记性不如烂笔头

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值