Java高级总结

java高级知识总结

string字符串

string字符串是一个常量,创建后值不可更改。

String str="hello";//这句话在执行时,创建一个hello字符串对象,将其地址保存在str变量中
str="abc";//这里看似是在改变字符串的值,实际是又创建了一个字符串对象abc,将其地址保存在str变量

string字符串的常用方法

常用构造方法作用
String()创建一个空白字符串对象,实际创建一个空字符数组
String(String str)创建一个指定的字符串对象,实际是创建一个形参字符串的副本
String(char[] list)创建一个指定字符数组的字符串对象。
String(byte[] list)按默认编码格式创建一个指定字节数组的字符串对象。
String(byte[] list,String charsetName)按指定的编码格式创建一个指定字节数组的字符串对象。

字符串相关测试题

//题目一
String str1="ab";
String str2=new String("ab");
String str3="a"+"b";//这个过程发生在编译阶段,实际保存的就是"ab"
String str4="你好"+str1;//这里的+,java会创建一个StringBuilder对象
String str5="你好"+str2;
String str6="你好ab";

System.out.println(str1==str2);//false
System.out.println(str1==str3);//true
System.out.println(str3==str2);//false
System.out.println(str4==str5);//false
System.out.println(str5==str6);//false

//题目二
//这两句话执行后,会创建几个对象
String str1="ab";
String str2="a"+"b";
//1个:存在于字符串常量池中的"ab"

//题目三
//这两句话执行后,会创建几个对象
String str1=new String("ab");//这句话执行后有两个new String()和"ab"
String str2=new String("ab");//这句话执行后有一个new String()
//3个:存在于字符串常量池中的“ab”和堆中的两个String对象

//题目四
//这两句话执行后,会创建几个对象
String str1 = new String("a");//2个,"a"和new String()
String str2 = str1 + "b";//2个,"b"和new StringBuilder()
//4个

字符串常用方法

方法名返回值作用
length()int获取字符串的长度
trim()String去除字符串首尾的所有空格
toLowerCase()String转换字符串为小写
toUpperCase()String转换字符串为大写
isEmpty()boolean判断是否为一个空字符串
getBytes()byte[]按默认编码格式将字符串转换为字节数组
toCharArray()char[]将字符串转换为字符数组
equalsIgnoreCase(String str)boolean忽略大小写比较指定字符串是否相同
equals(String str)boolean判断字符串是否相同
charAt(int index)char获取index位置上的字符串
indexOf(String str)int获取str第一次出现的位置,如果没有返回-1
lastIndexOf(String str)int获取str最后一次出现的位置,如果没有返回-1
contains(字符序列)boolean判断指定的字符序列(字符串)是否存在于原字符串中
concat(String str)String将str拼接到原字符串末尾
startsWith(String str)boolean判断是否以指定字符串开头
endsWith(String str)boolean判断是否以指定字符串结尾
substring(int index)String截取原字符串在[index,数组长度)区间内的字符。(从指定位置截取至末尾,包含指定位置)
substring(int from,int to)String截取原字符串在[from,to)区间内的字符。(从from截取至to,包含from不包含to)
split(String reg)String[]按指定字符串或正则表达式切分原字符串。如果指定内容不在末尾,n个指定字符能得到n+1个子串;如果指定内容在末尾,n个指定字符能得到n个子串(不包含末尾的无效字符)。
String.valueOf(参数)String将一个参数转换为字符串,参数可以是原始类型,也可以是任意对象。
replace(char oldChar,char newChar)String使用newChar替换oldChar

可变字符串

StringBuilder类

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

StringBuffer类

用于表示可变字符串的一个类,是线程安全的,建议在多线程环境下使用,效率略低于StringBuilder。

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

构造方法
常用构造方法作用
StringBuilder()创建一个大小为16的字符数组。类似于String str=“”;
StringBuilder(int capacity)创建一个指定大小的字符数组
StringBuilder(String str)创建一个str长度+16的字符串数组后,将str添加到其中。类似于String 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()翻转原字符串
可变字符串与不可变字符串之间的转换

有些方法如indexOf()、charAt()等,在String和StringBuilder中都存在,可以不用转换。
但有些方法如getBytes()、contains()等,只能通过String调用,这时就需要进行转换。

不可变字符串转换为可变字符串

通过创建一个可变字符串对象,将不可变字符串作为参数实现转换

//定义一个不可变字符串对象
String str="hello";
//创建一个可变字符串对象,将不可变字符串对象作为参数
StringBuilder sb = new StringBuilder(str);
可变字符串转换为不可变字符串

通过调用可变字符串的toString()方法实现转换

//创建一个可变字符串对象
StringBuilder sb = new StringBuilder("hello");
//调用toString()转换为String类型
String str=sb.toString();
比较String、StringBuilder、StringBuffer这三个类的区别

相同点

  • 这三个类都可以用于表示或操作一个字符串
  • 这三个类中有公共的方法,如indexOf()、charAt()等、
  • 这三个类都是被final修饰的类,不能被继承

不同点

  • String定义的字符串是一个常量,不可变;StringBuilder和StringBuffer定义的字符串是一个变量,可以改变。
  • String类中的方法调用后,会创建字符串副本,不会影响原字符串;可变字符串对象调用的方法,直接操作原字符串,会影响。
  • StringBuilder是非线程安全的,StringBuffer是线程安全的,其中的方法使用synchronized修饰为同步方法。在多线程情景下,需要使用StringBuffer。StringBuilder效率略高于StringBuffer。

Date类(表示日期与时间)

构造方法

常用构造方法说明
Date()创建当前瞬间的日期对象
Date(long l)创建指定毫秒数对应的日期对象。(从1970.1.1起经过了的毫秒数)
Date(int year,int month,int date)该构造方法已过时。创建指定年月日的日期对象。(年从1900年起经过的年数,月用0-11表示1-12月)

常用方法

常用方法
getTime()得到对应Date对象指定的毫秒数。
setTime(long l)设置毫秒数
after(Date when)判断调用日期对象是否在when之后
before(Date when)判断调用日期对象是否在when之前

SimpleDateFormat类(格式化日期)

构造方法

//参数为一个日期模板
SimpleDateFormat(String pattern);
//如定义这样的一个模板对象
SimpleDateFormat sdf=new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
//创建一个Date对象
Date now = new Date();
//调用格式化方法,参数为一个Date对象
String res=sdf.formart(now);
//输出的形式为2022/07/28
System.out.println(res);

常用方法

常用方法返回值作用
format(Date date)String将Date对象按日期模板转换为字符串
parse(String str)Date将满足日期模板的字符串转换为Date对象

Calendar类

用于表示日历的类。包含了很多日历相关的信息。
是一个抽象类,无法直接创建对象,可以通过该类的静态方法getInstance()获取该类的实例

//获取Calendar类的实例
Calendar c = Calendar.getInstance();

日历字段

该类中定义了很多被final和static修饰的日历字段,用于获取指定信息

说明
Calendar.YEAR获取年份
Calendar.MONTH获取月份(0-11表示1-12月)
Calendar.DATE获取日期
Calendar.DAY_OF_WEEK获取星期(1-7表示周天到周六)
Calendar.HOUR_OF_DAY获取二十四小时制的小时
Calendar.MINUTE获取分钟
Calendar.SECOND获取秒
Calendar.DAY_OF_MONTH本月中的第几天
Calendar.DAY_OF_YEAR本年中的第几天
Calendar.WEEK_OF_MONTH本月中的第几周
Calendar.WEEK_OF_YEAR本年中的第几周

常用方法

常用方法说明
get(日历字段)获取指定日历字段实际的值
getTime()获取对应的Date对象(Calendar转换为Date)
getMaximum(日历字段)获取指定日历字段支持的最大值。如Calendar.DATE支持最大值为31
getActualMaximum(日历字段)获取指定日历字段在实际时间下的最大值。如4月下Calendar.DATE实际最大值为30
getTimeZone()获取当前日历在实际时间下的时区信息
set(日历字段,值)将指定日历字段设置为指定值
set(年,月,日)设置指定的年月日,没有设置的部分沿用当前日期。

实现万年历练习

import java.util.Calendar;
import java.util.Scanner;

public class Test5 {
    public static void main(String[] args) {
        while (true){
            Calendar c = Calendar.getInstance();
            System.out.print("请输入年份");
            Scanner sc=new Scanner(System.in);
            c.set(Calendar.YEAR,sc.nextInt());
            System.out.print("请输入月份");
            c.set(Calendar.MONTH,(sc.nextInt()-1));
            System.out.println("一\t二\t三\t四\t五\t六\t日\t");
            int day = c.getActualMaximum(Calendar.DATE);
            int count=0;
            c.set(Calendar.DATE,1);
            int num= c.get(Calendar.DAY_OF_WEEK);
            for (int i = 1; i <= day; i++) {
                if (i==1){
                    if (num==1){
                        for (int x = 0; x < 6; x++) {
                            System.out.println(" \t");
                            count++;
                        }
                    }else{
                        for (int j = 0; j < (num-2); j++) {
                            System.out.print(" \t");
                            count++;
                        }
                    }
                }
                System.out.print(i+"\t");
                count++;
                if (count%7==0){
                    System.out.println();
                }
            }
        }
    }
}

三天上班两天摸鱼练习

package homework;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Scanner;

public class Test6 {
    /*
    从1号开始,上三天,耍两天
    /* 1,2,3,  4,5,  6,7,8,   9,10,   11,12,13, 14,15, 16,17,18,   19,20,   21,22,23,  24,25,  26,27,28,
         29,30,     31,1,2,    3,4, 5,6,7, 8,9 10*/
    public static void main(String[] args) {
        Calendar c = Calendar.getInstance();
        SimpleDateFormat sd = new SimpleDateFormat("yyyy/MM/dd");
        Scanner sc = new Scanner(System.in);
        System.out.print("请输入起始年份");
        int s_year = sc.nextInt();
        System.out.print("请输入起始月份");
        int s_month = sc.nextInt();
        System.out.print("请输入起始日");
        int s_d = sc.nextInt();
        c.set(s_year, s_month - 1, s_d);
        long start = c.getTime().getTime();
        System.out.print("输入查询年份");
        int year = sc.nextInt();
        System.out.print("输入查询月份");
        int month = sc.nextInt();
        System.out.print("输入查询日期");
        int day1 = sc.nextInt();
        c.set(year, month - 1, day1);
        long end = c.getTime().getTime();
        long day = (end - start) / 1000 / 3600 / 24;
        if (day >= 0) {
            int x = (int) ((day + 1) % 5);
            switch (x) {
                case 1:
                case 2:
                case 3:
                    System.out.println(sd.format(c.getTime()) + "\t该工作哦");
                    break;
                case 0:
                case 4:
                    System.out.println(sd.format(c.getTime()) + "\t可以摸鱼了");
                    break;
            }
        }else{
            System.out.println("这个时间已经过了哦");
        }
    }
}


异常

Error错误

如果出现XXXXXError,如StackOverflowError栈空间溢出时,无法通过额外的代码去解决,只能修改源码。

Exception异常

如果出现XXXXException,如NullPointerException空指针异常时,可以通过额外代码去避免。

运行时异常和非运行时异常

如果一个异常属于RuntimeException异常类的子类,称为运行时异常,可以通过编译,运行时可能抛出异常对象。
如果一个异常属于Exception异常类的子类,称为非运行时异常,无法通过编译,只有处理后才能编译运行。

  • 运行时异常(RuntimeException)
    可以通过编译,可以不用处理,但在运行时有可能会抛出异常。
常见运行时异常说明出现的情景
NullPointerException空指针异常用空对象null调用方法或属性
ArrayIndexOutOfBoundsException数组下标越界异常使用数组时,下标超出范围
ClassCastException对象转型异常Dog dog=(Dog)new Person();(Person类和Dog类不是继承关系)
InputMismatchException输入类型不匹配异常如需要int类型的数据,输入String类型的数据。
ArithmeticException算术运算异常0当分母
IllegalArgumentException非法参数异常调用方法的参数不合理

非运行时异常(除RuntimeException)
无法通过编译,必须要处理后才能编译运行。

常见非运行时异常说明出现的情景
IOException输入输出流异常当使用流对象
SQLException数据库SQL相关异常操作数据库时
FileNotFoundException文件未找到异常方法的参数为文件

处理异常Exception

只要处理Exception异常类的子类时,都称为处理异常。处理异常的目的就是为了保证程序正常运行,不要中断。

try-catch-finally语句
try{
    //可能出现异常的代码
}catch(异常类 异常对象){
    //如果出现异常对象,且与catch中的异常类匹配,则执行
}catch(异常类 异常对象){//后续的catch中的异常类型只能与之前的异常类型同级或是父类
    //如果出现异常对象,且与catch中的异常类匹配,则执行
}finally{
    //无论程序是否会抛出异常,一定要执行的代码
}

先执行try中的内容,当出现异常,与后续的catch进行匹配,如果匹配对应的异常类型或异常父类型,则执行大括号中的内容,最终一定执行finally中的内容

注意
  • 执行try中的内容时,当某行代码抛出异常后,不再执行try中该行代码后续的内容。
  • 无论try中是否会抛出异常,finally中的内容一定执行。通常finally用于释放资源。
  • 如果有多个catch,需要将异常子类放在最前,异常父类放在最后
  • try、catch、finally都不能单独使用,try需要配合catch或finally或catch和finally一起使用
  • try中定义的内容,无法在try之外的地方使用
  • try中如果有return,不影响finally的执行,并且finally优先于return执行
throws关键字

这种方式,可以让非运行时异常通过编译,定义方法的时候,声明可能抛出的异常

public class Test{
    public void fun() throws InterruptException{//在定义方法时,声明可能抛出的异常类型
        //如果直接写这句话,会报错,因为sleep()方法会抛出一个非运行时异常,必须要处理
        Thread.sleep(1000);
    }
}
throw和throws

throws表示用于方法声明异常。使用时写在方法的小括号之后:

public void fun() throws Exception{
  
}

throw用于手动抛出异常对象。使用时写在{}中,通常用于满足某种条件时,强制中断程序:

public void fun(){
    RuntimeException e =  new RuntimeException();
    throw  e;
}

自定义异常步骤

  • 定义一个类,继承某个异常类。
    ​ 如果继承的是RuntimeException,表示自定义的异常类属于运行时异常,该异常对象可以不用处理。
    ​ 如果继承的是非RuntimeException,表示自定义的异常类属于非运行时异常,该异常对象必须要处理。
  • [可选操作]定义一个无参数的构造方法,调用父类中无参的构造方法,定义一个带字符串参数的构造方法,调用父类带字符串参数的构造方法。

集合

集合的特点

  • 能保存一组数据,可以有序也可以无序
  • 集合的容量可变
  • 集合中可以保存不同类型的数据
  • 可以获取集合中实际的元素数量

集合框架(集合家族)

Iterable接口并不算严格意义上的集合的"根",它称为迭代器,用于遍历集合元素的一个工具接口。
所以集合的根接口为:Collection接口Map接口

Collection接口

核心的两个子接口:Set和List。
这两个接口都可以保存一组数据,Set接口保存数据时,是无序不重复的;List接口保存数据时,是有序可重复的。

List接口(有序可重复)

有序集合,元素可以重复,允许保存null,可以通过索引获取对应位置上的元素。
在接口中定义了一些操作元素的方法,如获取元素数量、添加、删除、替换、截取等。

ArrayList实现类
  • 采用数组实现的集合
  • 可以通过索引访问元素、可以改变集合大小。如果要在其中插入或删除元素时,会影响其余元素。该集合查询效率高、增删中间元素效率低。
  • 该集合对象中保存的元素,都是引用类型(对象的内存地址)。即使保存了123,其实不是保存的int类型的123,而是Integer类型的123.
构造方法
常用构造方法说明
ArrayList()创建一个无参的ArrayList对象。实际是创建了一个空的Object数组。在调用添加元素方法时,才会初始化该数组大小为10。
ArrayList(int capacity)创建一个指定容量的ArrayList对象,实际是创建一个指定大小的Object数组,如果参数为负,会抛出IllegalArgumentException异常
常用方法
常用方法作用返回值
add(Object obj)添加obj到集合的末尾boolean
add(int index,Object obj)添加obj到index索引上void
get(int index)得到指定index索引的元素Object
set(int index,Object obj)使用obj覆盖index索引的元素,返回被覆盖的元素Object
size()获取集合中的元素数量int
isEmpty()判断集合是否为空boolean
clear()清空集合void
remove(Object obj)删除指定obj对象boolean
remove(int index)删除指定Index索引的元素,返回被删除的元素Object
contains(Object obj)判断是否存在obj元素boolean
indexOf(Object obj)得到元素obj第一次出现的索引int
lastIndexOf(Object obj)得到元素obj最后一次出现的位置int
subList(int from,int to)得到[from,to)区间内的元素List
toArray()将集合转换为数组Object[]
LinkedList实现类
  • 采用双向链表实现的集合

  • 集合中保存的每个元素也称为节点,除首尾节点外,每个节点即保存了自己的数据,也保存了其前一个和后一个节点的地址

  • 如果在其中进行插入和删除的操作时,不影响其他元素的位置,只需要重新定义新节点的前后节点位置即可。

  • 如果要查询某个节点的索引,需要从头结点或尾结点开始一步步得到目标节点位置

  • 所以中间进行插入和删除的效率高,随机读取的效率低

构造方法
常用构造方法说明
LinkedList()创建一个空链表
常用方法

由于LinkedList实现了List接口,所有这里省略List接口中常用的方法,如add(Object obj)、Remove(Object obj)等。参考ArrayList中的方法

常用方法
addFirst()添加头结点
addLast()添加尾结点
getFirst()得到头结点
getLast()得到尾结点
remove()删除头结点
removeFirst()删除头结点
removeLast()删除尾结点
push(Object obj)添加头结点(入栈)
pop()得到并移除头结点(出栈)
peek()得到头结点
poll()删除头结点
offer(Object)添加尾结点
ArrayList和LinkedList的区别
  • 两者都是List接口的实现类,保存的元素有序可重复,允许保存null,拥有一些公共的方法,如size(),isEmpty(),subList(int from,int to)等
  • ArrayList采用数组实现,对于随机读取效率更高,通常用于查询;LinkedList采用双向链表实现,插入删除不影响其他元素位置,通常用于中间插入删除。
Set接口(无序不重复)

无序集合,元素不可以重复,允许保存null,没有索引。
在接口中定义了一些操作元素的方法,如获取元素数量、添加、删除、替换、截取等。

哈希表hash table

哈希表,也称为散列表,是一种数据结构,能更快地访问数据。
要保存的数据称为原始值,这个原始值通过一个函数得到一个新的数据,这个函数称为哈希函数,这个新数据称为哈希码,哈希码和原始值之间有一个映射关系,这个关系称为哈希映射,可以构建一张映射表,称为哈希表。在哈希表中,可以通过哈希码快速访问对应的原始值。

哈希码的特点
  • 如果两个对象的hashCode不同,这两个对象一定不同
  • 如果两个对象的hashCode相同,这两个对象不一定相同
    • 如**“通话""重地”**这两个字符串的hashCode相同,但是两个对象
    • hashCode相同,对象不同,称为哈希冲突
HashSet实现类
  • 采用哈希表实现
  • 元素不能重复,无序保存,允许保存null
  • 本质是一个HashMap对象,调用add()方法,实际调用的也是HashMap中的put()方法,参数作为put()方法的键,new Obejct()作为put()方法的值
HashSet添加数据的原理

if(两个对象hashCode相同&&两个对象equals相同){视为同一对象,不能添加}
每次向集合中添加元素时,先判断该元素的hashCode是否存在,

  • 如果不存在,视为不同对象,直接添加
  • 如果存在,再判断equals方法的结果
    • 如果为false,视为不同对象,直接添加
    • 如果为true,视为同一对象,不能添加
      可见不能添加的条件是两个对象的hashCode相同并且equals结果为true。如果每次都只判断equals的话,过程可能会很久,效率不高,如果每次只判断hashCode的话,有可能会有哈希冲突,所以先判断hashCode,再判断equals,既能保证效率又能保证添加进去的元素都是不相同的元素。
equals方法和hashCode的关系
  • 如果两个对象的equlas方法比较结果为true,在没有重写equals方法的前提下,hashcode相同吗?
    • 相同
  • 如果两个对象的hashCode不同,在没有重写equals方法的前提下,equals方法比较结果为?
    • false
  • 如果两个对象的hashCode相同,equals方法比较结果为?
    • 可能为true也可能是false
构造方法
常用构造方法说明
HashSet()创建一个默认的集合对象,实际是创建了一个大小为16,加载因子为0.75的HashMap对象
HashSet(int capacity)创建一个指定容量的集合对象,实际是创建了一个指定大小,加载因子为0.75的HashMap对象
HashSet(int capacity,float loadFactor)创建一个指定容量和指定加载因子的集合对象。
常用方法
常用方法说明
add(Object obj)添加元素到集合中
size()得到元素数量
isEmpty()判断集合是否为空
remove(Object obj)根据对象删除元素
contains(Object obj)判断是否存在某个元素
toArray()将集合转换为Object数组
clear()清空集合

如果想要保存的对象保证不重复,且无关顺序时,可以使用HashSet。

TreeSet实现类
  • 特殊的Set实现类,数据可以有序保存,可以重复,不能添加null元素
  • 采用红黑树(自平衡二叉树)实现的集合
    • 二叉树表示某个节点最多有两个子节点
    • 某个节点右侧的节点值都大于左侧节点值
  • 只能添加同一种类型的对象且实现了Comparable接口的对象
    • 实现Comparable接口后必须要重写compareTo方法
    • 每次调用添加时,参数会自动调用该方法
  • 添加的元素可以自动排序
  • compareTo方法的返回值决定了能否添加新元素和新元素的位置
    • 如果返回0,视为每次添加的是同一个对象,不能重复添加
    • 如果返回正数,将新元素添加到现有元素之后
    • 如果返回负数,将新元素添加到现有元素之前
构造方法
常用构造方法说明
TreeSet()实际会创建一个TreeMap对象
常用方法
常用方法作用
add(Object obj)添加元素到集合中
size()得到元素数量
isEmpty()判断集合是否为空
clear()清空集合
ceiling(Object obj)得到比指定元素大的最小元素
floor(Object obj)得到比指定元素小的最大元素
first()/last()得到第一个/最后一个元素
HashMap实现类
  • JDK1.8之后,HashMap的数据结构采用"数组+链表+红黑树"实现
    • 当没有哈希冲突时,元素保存到数组中
    • 如果哈希冲突,在对应的位置上创建链表,元素保存到链表中
    • 当链表元素数量大于8,转换为红黑树
  • 数据采用**"键值对"的形式保存,键称为key**,值称为value ,键不能重复,允许null,值没有限制,键和值都是引用类型
  • 在哈希表中,哈希码就是键,保存的数据就是值,可以通过键得到相应的值。
构造方法
常用构造方法说明
HashMap()创建一个空的集合对象,默认大小为16,加载因子为0.75
常用方法
常用方法作用
put(Object key,Object Value)添加一组键值对
get(Object key)根据键得到值
size()得到键值对的数量
clear()清空所有键值对
keyset()得到所有键的集合
values()得到所有值的集合
containtsKey(Object key)判断是否包含某个键
containsValue(Object value)判断是否包含某个值
remove(Object key)根据键删除键值对
泛型

一种规范,常用于限制集合中元素的类型,省去遍历元素时转型的过程。
在定义集合时,在接收集合变量的数据类型后,写上<数据类型>。
集合类或接口<数据类型> 集合变量名 = new 集合实现类<>();

Collections集合工具类
  • Collection是集合的根接口,定义了集合的方法
  • Collections是集合的工具,定义了很多静态方法,直接通过类名使用
常用方法
常用方法
Collections.shuffle(List list)打乱有序集合中的元素顺序
Collections.swap(List list,int i,int j)交换有序集合中i和j索引上的元素
Collections.replaceAll(List list,Object oldVal,Object newVal)将有序集合中的旧元素替换为新元素
Collections.fill(List list,Object obj)将有序集合使用obj填充
Collections.rotate(List list,int distance)将最后distance个元素放在最前
Collections.sort(List list)对有序集合中的元素进行排序,元素必须实现了Comparable接口
Collections.max(Collection list)得到有序集合中的最小元素,元素必须实现了Comparable接口
Collections.min(Collection list)得到有序集合中的最大元素,元素必须实现了Comparable接口
实现购物车练习
购物车类(Cart)
package homework1;

import java.util.HashMap;
import java.util.Scanner;
import java.util.Set;

public class Cart {
    HashMap<Goods, Integer> hm = new HashMap();

    public void add(Goods good, Integer num) {
        if (!hm.containsKey(good)) {
            hm.put(good, num);
            System.out.println("添加成功");
        } else {
            hm.put(good, hm.get(good) + num);
            System.out.println("添加成功");
        }
    }

    public void show() {
        double total_price = 0;
        for (Goods goods : hm.keySet()) {
            Integer value = hm.get(goods);
            double price = goods.getPrice() * value;
            total_price += price;
            System.out.println("商品:" + goods + "\t数量:" + value + "\t价值:" + price);
        }
        System.out.println("总价:" + total_price);
    }


    public void delete(Goods good) {
        if (hm.containsKey(good)) {
            hm.remove(good);
            System.out.println("删除成功");
            return;
        }
        System.out.println("删除失败");
    }

    public void empty() {
        System.out.println("确定清空购物车么?输入1确认,输入其他取消");
        if (new Scanner(System.in).nextInt() == 1) {
            hm.clear();
            System.out.println("清空完成");
            return;
        }
        System.out.println("取消成功");
    }
}
商品类(Goods)
package homework1;

import java.util.Objects;

public class Goods implements Comparable {
    private Integer id;
    private String brand;
    private String name;
    private Double price;


    public Goods() {
    }

    public Goods(Integer id, String brand, String name, Double price) {
        this.id = id;
        this.brand = brand;
        this.name = name;
        this.price = price;
    }

    @Override
    public String toString() {
        return "编号:" + id +
                ", 品牌:'" + brand + '\'' +
                ", 款式:'" + name + '\'' +
                ", 单价:" + price;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Goods goods = (Goods) o;
        return Objects.equals(id, goods.id) && Objects.equals(brand, goods.brand) && Objects.equals(name, goods.name) && Objects.equals(price, goods.price);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, brand, name, price);
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }


    @Override
    public int compareTo(Object o) {
        Goods good = (Goods) o;
        return this.id - good.id;
    }
}
商城类(Mall)
package homework1;

import java.util.TreeSet;

public class Mall {
    TreeSet<Goods> goods = new TreeSet<>();

    public void add(Goods good) {
        goods.add(good);
    }

    public void show() {
        for (Goods good : goods) {
            System.out.println(good);
        }
    }

    public Goods show_one_byId(int id) {
        for (Goods good : goods) {
            if (good.getId() == id) {
                return good;
            }
        }
        System.out.println("商品不存在");
        return null;
    }
}
Main
package homework1;

import java.util.Scanner;

public class Main {
    Goods g1 = new Goods(3, "耐克", "空军一号", 568.0);
    Goods g2 = new Goods(1, "匡威", "1970s", 348.0);
    Goods g3 = new Goods(2, "耐克", "aj1", 1568.0);
    Goods g4 = new Goods(1, "耐克", "空军一号", 568.0);
    Mall mall = new Mall();
    Cart cart = new Cart();

    public void buy() {
        mall.add(g1);
        mall.add(g2);
        mall.add(g3);
        mall.add(g4);
        System.out.println("请选择功能:\n1:购买商品\n2:查看购物车\n3:删除商品\n4:清空购物车\n0:退出");
        Scanner sc = new Scanner(System.in);
        switch (sc.nextInt()) {
            case 1:
                mall.show();
                System.out.println("请输入购买的商品编号");
                Integer input = sc.nextInt();
                System.out.println("请输入购买数量");
                Integer input1 = sc.nextInt();
                cart.add(mall.show_one_byId(input), input1);
                System.out.print("您当前购物车中:\n");
                cart.show();
                buy();
                break;
            case 2:
                cart.show();
                buy();
                break;
            case 3:
                System.out.println("请输入删除的商品编号");
                Integer id = sc.nextInt();
                cart.delete(mall.show_one_byId(id));
                buy();
                break;
            case 4:
                cart.empty();
                buy();
                break;
            case 0:
                break;
        }
    }

    public static void main(String[] args) {
        new Main().buy();
    }
}

file文件类

构造方法

常用构造方法说明
File(String pathName)根据文件的完整路径创建对象
File(String parent,String name)根据文件所在的父目录路径和自身文件名创建对象
File(File parent,String name)根据文件所在父目录文件对象和自身文件夹创建对象

常用方法

常用方法说明返回值
exists()判断文件是否存在boolean
isFile()判断是否为文件boolean
isDirectory()判断是否为目录boolean
getName()得到文件名String
getPath()得到文件所在相对路径String
getAbsolutePate()得到文件所在绝对路径String
getParent()得到父目录的名称String
getParentFile()得到父目录的对象File
lastModified()得到文件最后一次修改时间对应的毫秒数long
length()得到文件的所占字节大小long
isHidden()判断文件是否为隐藏boolean
list()得到目录下的第一层子文件名的字符串数组String[]
listFiles()得到目录下的第一层子文件的文件数组File[]
delete()删除某个文件或空目录boolean
mkdir()创建一个空目录boolean
renameTo(File newFile)将原文件命名为新文件(新文件可以指定新位置)

递归函数

public class Test3 {
    //递归
    public static void main(String[] args) {
        System.out.println(fun(20));
    }
    public static int fun(long n){
        if (n>2){
         return   fun(n-1)+fun(n-2);
        }
        return 1;
    }
}

IO

在Java中,流用于表示计算机硬盘与内存之间传输数据的通道
内存中的数据存入到硬盘中,称为写write,也称为输出
硬盘中的数据存入到内存中,称为读read,也称为输入

流的分类

InputStream字节输入流

FileInputStream文件字节输入流 ObjectInputStream对象字节输入流(反序列化)

OutputStream字节输出流

FileOutputStream文件字节输出流 ObjectOutputStream对象字节输出流(序列化)

Writer字符输出流

FileWriter文件字符输出流 **BufferedWriter缓冲字符输出流(包装流) ** OutputStreamWriter字节输出流转换为字符输出流(转换流)

Reader字符输入流

FileReader文件字符输入流 BuffedredReader缓冲字符输入流(包装流) InputStreamReader字节输入流转换为字符输入流(转换流)
按方向分类
输入流:InputStream、Reader
作用:将硬盘中的数据读取到内存中
输出流:OutputStream、Wrtiter
作用:将内存中的数据写入到硬盘中
按类型分
字节流:InputStream、OutputStream
作用:适用于非文本类型,如图片、文件等的读写
字符流:Reader、Writer
作用:适用于文本类型,尤值txt格式文件的读写
如要将硬盘中某个txt文件中的字符读取到程序中,使用Reader
如要将硬盘中的某个图片读取到程序中,使用InputStream

流的四个父类的特点
  • 以上四个类都在java.io包下,都是抽象类,不能直接创建其对象,要使用其子类
  • 这四个类都有close()方法,用于关闭流对象释放资源
  • 输出流(OutputStream和Write)都有flush()方法,用于将流中的数据到冲刷到硬盘中
    • 在使用输出流对象时,一定要调用flush()或close()方法后,才能真正将数据写到硬盘中
  • 输入流(InputStream和Reader)都有read()方法读取数据到内存中,输出流都有write()方法写入数据到硬盘中
  • 所有的流类中,以Stream结尾的,都是字节流,以Reader或Writer结尾的都是字符流
  • 读取硬盘中的数据时,使用输入流;将数据写入到硬盘中时,使用输出流
  • 读取或写入文本文件时,使用字符流;读取或写入非文本文件时,使用字节流

FileInputStream文件字节输入流

构造方法
常用构造方法说明
FileInputStream(String pathName)根据文件名创建文件字节输入流对象
FileInputStream(File file)根据文件对象创建文件字节输入流对象
常用方法
常用方法作用
read()读取一个字节。返回读取到的字节本身。
read(byte[] b)按字节数组读取。返回读取到的字节数量。读取到的内容保存在了字节数组中
close()关闭流对象

FileOutputStream文件字节输出流

构造方法
常用构造方法说明
FileOutputStream(String pathName)根据文件名创建文件字节输出流对象,写入内容时覆盖原内容
FileOutputStream(String pathName,boolean append)根据文件名创建文件字节输出流对象,写入内容时追加在原内容之后
FileOutputStream(File file)根据文件对象创建文件字节输出流对象,写入内容时覆盖原内容
FileOutputStream(File file,boolean append)根据文件对象创建文件字节输出流对象,写入内容时追加在原内容之后
常用方法
常用方法作用
write(int i)写入一个字节
write(byte[] bytes)写入一个字节数组
write(byte[] bytes,int off,int len)写入字节数组中从off开始的len个字节
flush()将数据冲刷到本地文件中
**close(
使用FileInputStream和FileOutputStream读写时的注意事项
  • 在使用read(byte[] bytes)方法时,每次都会读取指定数组大小的字节,将读取到的字节保存到数组中,该方法返回读取到的字节数量。如果最后一次读取的字节数不足字节数组大小时,只会用读取到的内容覆盖最前的几个元素。所以会导致读取到的内容多于实际内容。
  • 在使用write(byte[] bytes)方法时,会将字节数组中的所有内容写入到输出流中,在最后一次写入时,可能会写入多余的内容。所以在写入时,最好使用write(byte[] bytes,int off,int len)方法,表示将字节数组中的元素,从off开始,写入len个字节。

FileReader文件字符输入流

构造方法
常用构造方法说明
FileReader(String fileName)根据文件名创建文件字符输入流对象
FileReader(File file)根据文件对象创建文件字符输入流对象
常用方法
常用方法作用
ready()判断是否还有下一个字符
read()读取下一个字符,返回读取到的字符
read(char[] chars)按字符数组读取字符,返回读取到的字符数量,读取到的字符保存在了字符数组中
close()关闭流对象

FileWriter文件字符输出流

构造方法
常用构造方法说明
FileWriter(String fileName)按文件名创建字符输出流对象
FileWriter(String fileName,boolean append)按文件名创建字符输出流对象,允许追加写入
FileWriter(File file)按文件对象创建字符输出流对象
FileWriter(File file,boolean append)按文件对象创建字符输出流对象,允许追加写入
常用方法
常用方法作用
write(String str)按字符串写入
write(char[] c)按字符数组写入
write(String str,int off,int len)按字符串写入,从off开始写入len个字符
write(char[] c,int off,int len)按字符数组写入,从off开始写入len个字符
flush()冲刷流中的数据到硬盘中(必须调用该方法或close方法后,才能真正写入)
close()关闭流对象

BufferedReader字符缓冲输入流

自带字符数组(缓冲区)的字符输入流。默认字符数组大小为8192,每次最多读取8192个字符,在读取纯文本文件(txt或md等)时,首选该类。

构造方法
常用构造方法说明
BufferedReader(Reader reader)创建一个带有缓冲区(字符数组)的字符输入流对象,默认缓冲区大小为8192。参数注意是一个Reader类型的对象,但Reader是一个抽象类,所以要使用其子类,如FileReader,然后在FileReader中定义要读取的文件信息
BufferedReader(Reader reader,int size)创建一个指定缓冲区(字符数组)大小的字符输入流对象
常用方法
常用方法作用
ready()判断是否还有字符
readLine()读取整行字符
close()关闭流对象

BufferedWriter字符缓冲输出流

构造方法
常用构造方法说明
BufferedWriter(Writer writer)创建一个带有缓冲区(字符数组)的字符输出流对象,参数为一个Writer对象,但Writer是一个抽象类,所以要使用其子类,如FileWriter对象,在FileWriter中定义要写入的文件
BufferedWriter(Writer writer,int size)创建一个指定缓冲区(字符数组)大小的字符输出流对象
常用方法
常用方法作用
write(String str)写入一个字符串
newLine()换行
flush()将输出流中的字符冲刷到文件中
close()关闭流对象

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

序列化:将对象转换为文件的过程
被序列化的对象,必须要实现Serializable接口
这个接口是一个特殊的接口,没有定义方法,只是给这个类打上标记,表示该类可以被序列化。

构造方法
常用构造方法说明
ObjectOutputStream(OutputStream os)创建一个对象字节输出流对象,参数为一个字节输出流对象,由于是抽象类,所以要使用其子类,如FileOutputStream对象,在其中定义要写入的文件。
常用方法
常用方法作用
writeObject(Object obj)将一个对象写入到本地文件中
close()关闭流对象

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

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

构造方法
常用构造方法说明
ObjectInputStream(InputStream is)创建一个对象字节输入流对象,参数为一个字节输入流对象,由于是抽象类,所以要使用其子类,如FileInputStream对象,在其中定义要读取的文件
常用方法
常用方法作用
readObject()读取序列化后的文件,返回对象Object
close()关闭流对象

转换流

OutputStreamWriter

将字节输出流转换为字符输出流

InputStreamReader

将字节输入流转换为字符输入流

网络编程

InetAddress类

表示IP对象的一个类

Socket类和ServerSocket类

都属于Socket(套接字)对象,表示网络中的某个端点。

  • ServerSocket指服务器端
  • Socket指普通端点

进程和线程

进程Process

进程就是操作系统中执行的程序。一个进程就是一个执行的程序实体。
每个运行中的进程,都有属于它独立的内存空间。各个进程之间互不影响。

线程Thread

线程是一个进程中的执行单元,一个进程中可以有多个线程。
多线程之间,可以访问同一个进程中的资源。
每个线程都有一个独立的栈空间。这些线程所在的栈空间位于同一个进程空间中。

多线程

如果一个进程中,同时在执行着多个线程,就称为多线程。
其实每个执行的Java程序,都是多线程的,虽然看似只有main方法所在的主线程在执行,其实还有一个gc线程(守护线程)。

并行和并发

并行

多个进程各自执行,称为并行。

并发

多个线程同时执行,称为并发。

同步和异步

同步

所有的任务排队执行,称为同步执行。

异步

在执行任务A的同时,执行任务B,称为异步执行。

Java中的线程

Java中,线程以对象的形式存在。
Thread表示线程类。

获取线程对象

  • 获取当前正在运行的线程对象

    //获取当前运行的线程对象
    Thread ct = Thread.currentThread();
    
  • 创建一个线程对象

    构造方法

    常用构造方法说明
    Thread()创建一个默认的线程对象
    Thread(String name)创建一个指定线程名的线程对象
    Thread(Runnable target)将一个Runnable对象包装为线程对象
    Thread(Runnable target,String name)将一个Runnable对象包装为线程对象同时命名

常用方法

常用方法作用
setName(String str)设置线程名称
setPriority(int i)设置线程优先级(1~10),数字越大优先级越高,线程越先执行完
setDaemon(boolean f)是否将该线程设置为守护线程
getId()获取线程ID
getName()获取线程名,主线程名默认main,自定义线程名默认Thread-N
getPriority()获取线程优先级
getState()获取线程状态
isDaemon()判断该线程是否属于守护线程
start()启动线程
run()线程启动后执行的方法
Thread.currentThread()获取当前运行的线程对象
Thread.sleep(long m)设置当前线程休眠m毫秒
Thread.yield()线程让步,让其他线程执行

实现多线程

方式一:继承Thread类

  • 1.让某个类成为Thread类的子类
  • 2.重写Thread类中的run方法,将要让该线程执行的内容写在该方法中
  • 3.创建Thread类的对象后,调用start()方法,启动线程

方式二:实现Runnable接口(建议使用)

由于Java中是单继承,所以如果某个类已经使用了extends关键字去继承了另一个类,这时就不能再使用extends继承Thread类实现多线程,就需要使用实现Runnable接口的方法实现多线程。

  • 1.自定义一个类,实现Runnable接口
  • 2.重写run方法,将要多线程执行的内容写在该方法中
  • 3.创建Thread线程对象,将自定义的Runnable接口实现类作为构造方法的参数
  • 4.调用线程对象的start()方法启动线程

方式三:使用匿名内部类

如果不想创建一个Runnable接口的实现类,就可以使用匿名内部类充当Runnable接口的实现类

线程的生命周期

线程的初始化到终止的整个过程,称为线程的生命周期

新生状态

当线程对象被实例化后,就进入了新生状态。new Thread()

就绪状态

当某个线程对象调用了start()方法后,就进入了就绪状态。

在该状态下,线程对象不会做任何事情,只是在等待CPU调用。

运行状态

当某个线程对象得到运行的机会后,则进入运行状态,开始执行run()方法。

不会等待run()方法执行完毕,只要run()方法调用后,该线程就会再进入就绪状态。这时其他线程就有可能穿插其中。

阻塞状态

如果某个线程遇到了sleep()方法或wait()等方法时,就会进入阻塞状态。

sleep()方法会在一段时间后自动让线程重新就绪。

wait()方法只有在被调用notify()或notifyAll()方法唤醒后才能进入就绪状态。

终止状态

当某个线程的run()方法中所有内容都执行完,就会进入终止状态,意味着该线程的使命已经完成。

多线程访问同一资源

可能出现的问题

如多线程卖票,多线程取钱,类似的多个线程在操作同一个资源时,实际操作的结果和预想结果不符。
如票实际卖出的大于原本的,取出的钱大于本金等

出现问题的原因

由于线程调用start()方法后,就进入就绪状态,如果获得了CPU的使用权,开始调用run()方法,调用run方法后,马上就会重新回到就绪状态,所以不会等待run()方法执行结束,在执行run()方法的时候,可能其他线程也可能获取CPU的使用权,从而开始执行run()方法。
当前所有线程是在异步执行。

如何解决

让线程同步执行(排队)即可。就能在某个线程执行run方法的时候,让其他线程等待run()方法执行完毕

synchronized关键字

这个关键字可以修饰方法或代码块。

修饰方法

写在方法的返回值前。这样该方法就称为同步方法,在执行的时候,其他线程要排队等待该方法执行完毕后才执行。

修饰代码块

写在{}前,这样这段{}中的内容称为同步代码块,在执行的时候,其他线程要排队等待该方法执行完毕后才执行。
原理
每个对象都默认有一把"锁",当某个线程运行到被synchronized修饰的方法时,该对象就会拥有这把锁,在拥有锁的过程中,其他线程不能同时访问该方法,只有等待其执行结束后,才会释放这把锁。
使用synchronized修饰后拥有的锁称为"悲观锁",在拥有的时候,不允许其他线程访问。
方法被synchronized修饰后,成为了同步方法,就会让原本的多线程成为了单线程(异步变为同步),虽然效率变低,但是数据安全是首位。

多线程相关测试题

  • 实现多线程的方式?
    • 继承Thread类
    • 实现Runnable接口后,包装为Thread对象
    • 使用匿名内部类
  • 为什么说StringBuilder是非线程安全的
  • 什么叫死锁?怎么产生?如何解决?
    如有两个人吃西餐,必须要有刀和叉才能吃饭,只有一副刀叉。
    如果A拿到了刀,B拿到了叉,互相都在等待另一个工具,但都不释放自己的,这时就会造成死锁的局面,既不结束,也不继续。

模拟死锁出现的原因

定义两个线程类,线程A先获取资源A后,再获取资源B;线程B先获取资源B后,再获取资源A。
如果对A和B对使用了synchronized进行同步,就会在线程A获取资源A时候,线程B无法获取资源A,
相反线程B在获取资源B的时候,线程A无法获取资源B,所以两个线程都不会得到另一个资源。

死锁的解决方式一:

让线程A和线程B获取资源A和资源B的顺序保持一致。

死锁的解决方式二:

让线程A和线程B获取资源A和资源B之前,再获取第三个资源,并对其使用synchronized进行同步,
这样线程A在获取第三个资源后,将所有事情执行完后,线程B才能继续。

网络编程服务器端

public class Server {
    public static void main(String[] args) throws IOException {
        InetAddress ip = InetAddress.getByName("192.168.3.20");
        System.out.println(ip);
        ServerSocket server = new ServerSocket(99, 20, ip);
        Socket client = server.accept();
        FileOutputStream fos = new FileOutputStream(new File("D:\\信息12.zip"));
        InputStream is=client.getInputStream();
        byte[] bytes = new byte[1024 * 1024 * 16];
        int count= is.read(bytes);
        while (count!=-1){
            fos.write(bytes);
            count=is.read(bytes);
        }
        is.close();
        fos.close();
    }
}

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值