java基础(4)
2021/6/27 22:24:12
本文主要是介绍java基础(4),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
java.lang.Object类 类Object是类层次结构的根(最顶层)类。每一类都是用Object作为超(父)类。所有对象(包括数组)都实现这个类的方法。 String toString() 返回这个类的方法,打印对象的信息。重写前打印的是包名类名@地址值;重写后打印的是对象中的属性值。 直接打印对象名字其实就是调用对象的toString()方法p=p.toString(); 看一个类是否重写了toString方法,直接打印这个类对应对象的名字即可,如果没有重写toString方法,那么打印的就是对象的地址值(默认) 如果重写toString方法,那么就按照重写的方法打印。 boolean equals(Object obj)指示其他某个对象是否与此对象"相等"。重写前比较对象的地址值;重写后比较的是对象中的属性值。 Object类 equals方法的源码: public boolean equals(Object obj){ return (this == obj); } 参数:Object obj:可以传递任意的对象 方法体: ==:比较运算符,返回的就是一个布尔值 true,false 基本类型比较值,引用类型比较对象的地址值 this是调用equals方法的那个对象,obj是传过来的参数 Objects类的equals方法:对两个对象进行比较,防止空指针异常。比较两个对象是否相同,但是加了一些健壮性的判断。 public static boolean equals(Object a, Object b) {return (a == b) || (a != null && a.equals(b));} java.util.Date:表示日期和时间的类 类Date表示特定的瞬间,精确到毫秒。 毫秒:千分之一秒 1000毫秒=1秒; 特定的瞬间:一个时间点,一刹那时间。 毫秒的作用:可以对时间和日期进行计算。 2099-01-03到2088-01-01中间一共多少天 可以日期转换为毫秒进行计算,计算完毕,再把毫秒转换成日期。 把日期转换成毫秒: 当前的日期:2088-01-01 时间原点(0毫秒):1970年1月1日00:00:00(英国格林威治) 就是计算当前日期到时间原点之间一共经历了多少毫秒(374267540068L) 注意: 中国属于东八区,会把时间增加8个小时1970年1月1日08:00:00 把毫秒转换成日期: 1天=24*60*60=68400秒=68400*1000=68400000毫秒 System.out.println(System.currentTimeMillis());//获取当前系统时间到1970年1月1日00:00:00经历了多少毫秒 Date类的空参数构造方法: Date()获取的就是当前系统的日期和时间 Date类的带参数构造方法: Date(long date)传递毫秒值(long类型值),把毫秒值转化为Date日期。(从1970起) long getTime() 把日期转换为毫秒(相当于System.CurrentTimeMillis()) 返回自1970年1月1日00:00:00GMT以来此Date对象表示的毫秒数。 String toLocalString(); 根据本地格式转化日期对象。 不仅仅是Java,几乎所有的语言的时间都是从这一刻开始算起的。原因:java起源于UNIX系统,而UNIX认为1970年1月1日0点是时间纪元。 java.text.DateFormat:是时间/日期格式化子类的抽象类 作用: 格式化(也就是日期->文本)、解析(文本->时间) 构造方法: SimpleDateFormat(String s); 根据指定模板传进日期格式化对象。 成员方法: String format(Date date)按照指定的模式,把Date日期,格式化为符合模式的字符串。 1.创建SimpleDateFormat对象,构造方法中传递的模式。 2.调用SimpleDateFormat对象中的format,按照构造方法中指定的模式,把Date日期格式化为符合模式的字符串(文本) Date parse(String source)把符合模式的字符串,解析为Date日期 1.创建SimpleDateFormat对象,构造方法中传递的模式。 2.调用SimpleDateFormat对象中的parse,把符合构造方法中模式的字符串,解析为日期。 DateFormat类是一个抽象类,无法直接创建对象使用,但可以使用Dateformat的子类 java.text.SimpleDateFormat extends DateFormat 构造方法: SimpleDateFormat(String pattern) 用给定的模式和默认语言环境的日期格式符号构造SimpleDateFormat。 参数: String pattern:传递指定的模式 模式: 区分大小写 y年 M月 d天 H时 m分 s秒 写对应的模式,会把模式替换为对应的日期和时间"yyyy-MM-dd HH-mm-ss" "yyyy年MM月dd日 HH时mm分ss秒" 注意:模式中的字母不能更改,连接模式的符号可以更改。 java.util.Calendar类:日历类 Calendar类是一个抽象类,里面提供了很多操作日历字段的方法(YEAR、MONTH、DAY_OF_MONTH、HOUR) Calendar类无法直接创建对象使用,里面有一个静态方法叫getInstance(),该方法返回了Calendar类的子类对象 static Calendar getInstance() 使用默认时区和语言环境获得一个日历。 java.util.Calendar类的成员方法: public int get(int field):返回给定日历字段的值。 public void set(int field,int value):将给定的日历字段设置为给定的值。(set有一个重载方法可以同时设置年月日) public abstract void add(int field,int amount):根据日历的规则,为给定的日历字段添加或者减去指定的时间量。 int field:传递指定的日历字段 int amount:增加/减少的值(正、负) public Date getTime():返回一个表示此Calendar时间值(从历元到现在的毫秒偏移量)的Date对象;将日历对象转换为日期对象。 java.lang.System类提供了大量的静态方法,可以获取与系统相关的信息或系统级操作,在System类的API文档中,常用的方法有: public static long currenTimeMIllis():返回以毫秒为单位的当前时间。(返回的时间值可以当时间戳或者文件名进行使用) public static void arrayCopy(Object src,int srcPos,Object edst,int desPos,int length):将数组中指定的 数据拷贝到另一个数组中。 java.lang.StringBuffer类 字符串缓冲区,可以提高字符串的操作效率(看成一个长度可以变化的字符串) 底层也是一个数组,但是没有被final修饰,可以改变长度: byte[] value=new byte[16]; StringBuilder在内存中始终是一个数组,占用空间少,效率高。如果超出了StringBuilder的容量,会自动地扩容。 构造方法: public StringBuilder():构造一个空的StringBuiler容器; public StringBuilder(String str):构造一个StringBuiler容器,并将字符串添加进去。 成员方法: public StringBuilder append(...):添加任意类型的字符串形式,并返回当前对象自身。使用append方法无需接收返回值 参数:可以是任意类型。可以链式编程 StringBuilder reverse();反转内容,使用reverse方法无需接收返回值 String toString();将缓存区内容转换为字符串。 StringBuiler和String可以相互转换: String-》StringBuilder :可以使用StringBuilder的构造方法StringBuilder(String str)构造一个字符串生成器, 并初始化为指定的字符串内容。 StringBuilder-》String:可以使用StringBuilder中的toString 方法 public String toString():当前StringBuilder对象转换为String对象。 包装类:基本数据类型的数据,使用起来非常地方便,但是没有对应的方法来操作这些数据,所以我们可以使用一个类,把基本类型数据包装起来, 这个类叫包装类,在包装类中可以定义一些方法,用来操作基本类型的数据。 装箱:把基本类型的数据,包装到包装类中(基本类型的数据-》包装类) 构造方法: Interger(int value) 构造一个新分配的Integer对象,它表示指定的int值 Integer(String s) 它表示String参数所指的int值; 传递的字符串,必须是基本类型的字符串否则会抛出异常 静态方法: static Integer valueOf(int i)返回一个表示指定的 int值的Integer实例 static String valueOf(String s) 返回保存指定的String 的值的Integer对象。 拆箱:在包装类中取出基本类型的数据(包装类-》基本数据类型) 成员方法:int intValue() 以int类型返回该Integer的值。 自动装箱与拆箱: 基本类型的数据和包装类之间可以自动的相互转换。JDK1.5之后出现的新特性。 自动装箱:直接把int类型的整数赋值给包装类: Integer in=1;就相当于 Integer in=new Integer(1); 自动拆箱:in是包装类无法直接参与晕眩,可以自动转换为基本类型的数据,再参与运算in+2;相当于in.inValue() +2=3; in=in+2;就相当于in=new Integer(3)自动装箱 ArrayList集合无法直接储存整数,可以储存Integer包装类 ArrayList<Integer> list=new ArrayList<>(); list.add(1);//自动装箱相当于list.add(new Integer(1)); int a=list.get(0);//自动拆箱相当于 list.get(0).intValue(); 基本类型转换为String: 方法一: 基本类型直接与""相连接即可;如:34+"" 方法二: 使用包装类中的静态方法 static String toString(int i)返回一个指定整数的String对象。 方法三:使用String类中的静态方法: 使用包装类中的静态方法static String valueOf(int i)返回int参数的字符串表示形式 String转换成对应的基本类型:除了Character类之外,其他所有包装类都具有parseXxx 静态方法可以将字符串参数转换为对应的基本类型: Integer类: static int parseInt(String s)集合和数组的区别: 集合是java提供的一种容器,可以用来储存多个数据。数组的长度是固定的,集合的长度是可变的。 数组中储存的是同一种类型的元素,可以储存基本数据类型值。集合储存的都是对象。而对象的类型可以是不一致的。在开发中一般当对象多的时候,使用集合进行储存。 集合框架的学习方式: 1.学习顶层接口/抽象类中共性的方法,所有子类都可以使用。 2.使用底层:顶层不是接口就是抽象类,无法创建对象使用底层的子类创建对象使用。 Collection接口 定义的是所有单列集合中共性的方法;所有的单列集合都可以使用共性的方法,没有带索引的方法。有两个子接口List和Set List接口: 1.有序的集合(存储和取出元素顺序相同) 2.允许存储重复的元素 3.有索引,可以使用普通的for循环遍历 List接口中带索引的方法(特有): public void add(int index,E element):将指定的元素添加到该集合指定的位置上。 public E get(int index):返回集合中指定位置的元素。 public E remove(int index):移除列表中指定位置的元素。返回的是被移除的元素。 public E set(int index,E element):用指定元素替换集合中指定位置的元素,返回值是更新前的元素。 注意:操作的时候,一定要小心索引越界 list集合遍历有3种方式: 1.使用普通的for循环和get()方法 2.使用迭代器 3.使用增强for循环 Vector集合:JDK1.0版本,底层也是数组实现,同步,单线程,比较慢 ArrayList集合 :底层是数组实现的,查询快增删慢 ;不同步,多线程 LinkedList集合:底层是链表实现的,查询慢,增删快;不同步,多线程 里面含有大量操作首尾元素的方法,使用LinkedList集合特有的方法不能使用多态 : public void addFirst(E e):将指定元素插入到此列表的开头。 public void addLast(E e):将指定元素插入到此列表的结尾。相当于add() public void push(E e):将元素推入此列表所表示的堆栈。相当于addFirst() public E getFirst():返回此列表的第一个元素。 public E getLast():返回此列表的最后一个元素。 public E removeFirst():移除并返回此列表的第一个元素。 public E removeLast():移除并返回此列表的最后一个元素。 public E pop():从此列表所在的堆栈处弹出第一个元素。相当于removeFirst() public boolean isEmpty():如果列表不包含元素,则返回true。 public void clear():清空集合中的元素,再获取集合中的元素会抛出NoSuchElementException Set接口 1.不允许存储重复元素 2.没有索引(不能使用普通的for循环遍历) 3.无序的集合(储存和取出元素的顺序有可能不一致) 遍历方式:1.使用迭代器遍历set集合 2.使用增强for循环遍历set集合 TreeSet集合:底层是二叉树实现的,一般用于排序 HashSet集合:底层是哈希表+(红黑树)实现的,无索引,不可以存储重复元素,存取无序;底层是一个哈希表结构(查询的速度非常的快) LinkedHashSet集合:底层是哈希表+链表实现的,无索引,不可以存储重复元素、可以保证存储顺序。 boolean add(E e): 向集合中添加元素 boolean remove(E e); 删除集合中的某个元素; void clear(); 清空集合所有的元素 boolean contains(E e):判断集合中是否包含某个元素。 boolean isEmpty(); 判断集合是否为空 int size() : 获取集合的长度 Object [] toArray(); 将集合转换成一个数组 java.util.Iterator接口:迭代器(对集合进行遍历) 两个常用的方法: boolean hasNext() 如果仍有元素可以迭代,则返回true 判断集合中还有没有下一个元素,有就返回true,没有就返回false; E next() 返回迭代的下一个元素。 取出集合中的下一个元素。(没有元素,则会抛出NoSuchElementException没有元素异常) 它做了两件事:1.取处下一个元素2.会把指针向后移动一位。 Iterator迭代器是一个接口,无法直接使用,需要使用Iterator接口的实现类对象,获取实现类的方法比较特殊。 Collection接口中有一个方法,叫iterator(),这个方法返回的就是迭代器的实现类对象。 Iterator<E> iterator() 返回在此 collection 的元素上进行迭代的迭代器。获取迭代器的实现类对象,并且会把指针(索引)指向集合的-1索引 迭代器的使用步骤: 1.使用集合中方法iterator() 获取迭代器的实现类对象,使用Iterator接口接收(多态)。 Collecyion<E> coll=new ArrayList<E>(); Iterator<E> it=coll.iterator(); 2.使用Iterator接口中的方法hasNext判断还有没有下一元素。 3.使用Iterator接口中的方法next出集合中的下一个元素。 增强for循环:底层使用的也是迭代器,使用for循环的格式,简化了迭代器的书写,是JDK1.5之后出现的新特性。 Collection<E> extends Iterable<E>:所有单列集合都可以使用增强for循环 public interface Iterable<T> 实现这个接口允许对象成为"foreach"语句的目标;增强for循环:用来遍历集合和数组 格式:for(集合/数组的数据类型 变量名:集合名/数组名){ System.out.print(变量名); } 泛型:是一种位置的数据类型,当我们不知到使用什么数据了类型的时候,可以使用泛型; 泛型也可以看出是一种变量,用来接收数据类型: E e:Element 元素 T t:Type 元素 ArrayList集合在定义的时候,不知道集合都会存储什么类型的数据,所以类型使用泛型。(不写<E>默认为Object类型) 创建集合对象,使用泛型: 好处:1.避免了类型转换的麻烦,储存的是什么类型,取出的就是什么类型; 2.把运行期异常(代码运行之后会抛出的异常),提升到了编译期(写代码的时候会报错) 弊端:泛型是什么类型,只能储存什么类型的数据。 创建集合对象,不使用泛型: 好处:集合不使用泛型,默认的类型就是Object类型,可以储存任意类型的数据。 弊端:不安全,会引发异常。 比如向下转型,调用子类方法出错:String str=(String)obj; System.out.println(str.length()); java.base/java.lang.Integer cannot be cast to java.base/java.lang.String 定义一个含有泛型的类:public class GenericClass<E>{ private E name;...} 定义含有泛型的方法:泛型定义在方法的修饰符和返回值类型之间。 格式: 修饰符 <泛型> 返回值类型 方法名(参数列表(使用泛型)){方法体} public <M> void method01(M m){System.out.println(m);} 含有泛型的接口定义格式: 修饰符 interface 接口名<代表泛型的变量>{ } 例如: public interface MyInterface<E>{ public abstract void add(E e);} 含有泛型的接口,第一种使用方式:定义接口的实现类,实现接口,指定接口的泛型。 Scanner类实现了Iterator接口,并指定接口的泛型为String,所以重写的next方法泛型默认就是String public final class Scanner implements Iterator<String>{ public String next(){...}} 含有泛型的接口,第二种使用方式:接口使用什么泛型,实现类就使用什么泛型,类跟着接口走,就相当于定义了含有泛型的类,创建 对象的时候确定泛型的类型。 public interface list<E>{ boolean add(E e); E get(int index); } public class ArrayList<E> implements list<E>{ public boolean add(E e){} public E get(int index){} } 泛型通配符: 当使用泛型通配符或着接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示,但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合元素自身方法无法使用。 只能接受数据,不能往该集合中存储数据;不能创建对象使用,只能作为方法的参数使用(泛型没有继承的概念) 定义一个方法,能遍历所有类型的ArrayList集合,这时候我们不知道ArrayLilst集合使用什么数据类型,可以泛型的通配符?来接收数据类型 泛型的上限限定: ?extends E 代表使用的泛型只能是E类型的子类/本身 泛型的下限限定: ?super E 代表使用的泛型只能是E类型的父类/本身
数据结构: 栈:先进后出,入口和出口在同一侧; 队列:先进先出; 数组:查询快:数组的地址是连续的,我们可以通过数组的首地址找到数组,通过数组的索引可以快速查找到某一个元素。 增删慢:数组的长度是固定的,我们要增加/删除一个元素必须创建一个新数组,把原数组的数据复制过来,原数组会在内存中被销毁 在堆内存中,频繁的创建数组,复制数组中的元素,销毁数组,效率低下 链表:查询慢:链表中的地址不是连续的,每一次查询元素,都必须从头开始查询 增删快:链表结构,增加/删除一个元素,对链表的整体结构没有影响,所以增删快。 链表中的每一个元素也称之为一个节点,一个节点包含了一个数据源(储存数组),两个指针域(储存地址) 单向链表:链表中只有一条链子。不能保证有元素的顺序(存储元素和取出元素的顺序有可能不一致)。 双向链表:链表中有两条链子,有一条是专门记录元素的顺序,是一个有序的集合 二叉树:分支不能超过两个 排序树/查找树:猜数字小游戏1-100之间的数字,从50开始猜,一下减去一半,在二叉树的基础上,元素是有大小顺序的,左子树小,右子树大 平衡树:左孩子和右边孩子数相等。 不平衡树:左孩子!=右孩子 红黑树: 特点趋于平衡树,查找的速度非常快,查找叶子节点最大次数和最小次数不能超过2倍。 约束: 1.节点可以是红色的或者是任意黑色的 2.根节点是黑色的。 3.叶子节点(空节点)是黑色的 4.每一个红色的节点的子节点都是黑色的 5.任何一个节点到其每一个叶子节点的所有路径上黑色节点数相同。 哈希值:是一个十进制的整数,由系统随机给出(就是对象的地址值,是一个逻辑地址,是模拟出来的地址,不是数据储存的逻辑地址)在Object类有一个方法,可以获取对象的哈希码值。 int hashCode() 返回该对象的哈希码值。 hashCode方法的源码: public native int hashCode(); native:代表该方法调用的是本地操作系统的方法 String类的哈希值:String类重写Object类的hashCode方法。 toString方法中也用到了hashcode()方法:return getClass().getName+"@"+Integer.toNextString(hashCode()); HashSet集合存储数据的结构(哈希表) jdk1.8版本之前:哈希表=数组+链表 jdk1.8版本之后:哈希表=数组+链表; 哈希表=数组+红黑树(提高查询的速度) 哈希表的特点:速度快 储存数据到集合中,先计算元素的哈希值。 数据结构:把元素进行分组(相同哈希值的元素是一组) 链表/红黑树 把相同哈希值的元素连接到一起。 如果链表的长度超过了8位,那么就会把链表转换为红黑树(提高查询的速度 ) set集合存储元素不重复的前提:存储的元素必须重写hashCode方法和equals方法;Set集合在调用add方法的时候,add方法会调用元素的hashCode方法和equals方法,判断元素是否重复,如果重复就不会储存到集合中。 java.util.Collections是集合的工具类,用来对集合进行操作,部分方法如下: public static <T> boolean addAll(Collection<T> c,T...elements):往集合中添加一些元素。 public static void shuffle(List<?> list)打乱集合顺序 public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。 注意:sort(List<T> list)使用前提:被排序的集合里面存储的元素必须实现Comparable,重写接口中comparable定义排序的规则。 Comparable接口的排序规则:自己(this)- 参数:升序 public static <T> void sort(List<T> list,Comparator<? super T>):将集合中元素按照指定规则排序。 Comparator和ComParable的区别: Comparable:自己(this)和别人(参数)比较,自己需要实现Comparable接口,重写比较的规则compareTo方法 Comparator:相当于找到了一个第三方的裁判,比较两个 Collections.sort(arr, new Comparator<People>() { @Override public int compare(People o1, People o2) { int result=o1.getAge()-o2.getAge(); if(result==0){ return o1.getName().charAt(0)-o2.getName().charAt(0); } return o1.getAge()-o2.getAge(); } });
getClass()返回Class类型的对象。 返回的是内存中实实在在存在的 这个类本身 创建对象: Person p = new Person(1,”刘德华”); 返回Class类型的对象: Class c = p.getClass(); 此时c是Class类型,Class提供了一系列的方法来获取类的相关信息,可以通过对象c来获取Person的信息。比如,获取Person这个类的类名。 HashSet存储自定义类型,set集合保证元素唯一:存储的元素(String,Integer,Student,Person),必须重写hashCode方法和equals方法 java.util.LinkedHashSet集合 extends HashSet集合 LinkedHashSet集合特点: 底层是一个哈希表(数组+链表/红黑树)+链表:多了一条链表(记录了元素的储存顺序),保证元素有序。 可变参数:是JDK1.5之后出现的新特性。 使用前提:定义方法时使用,当方法的参数列表数据类型已经确定,但是参数的个数不确定,就可以使用可变参数,使用格式: 修饰符 返回值类型 方法名(数据类型...变量名){} 可变参数原理:可变参数底层就是一个数组,根据传递参数个数不同,会创建不同长度的数组,来存储这些参数传递的参数个数,可以是0个(不传递), 1,2...多个。 注意事项:1.方法的参数列表,只能有一个可变参数。 2.如果方法的参数有多个,那么可变参数必须写在参数列表的末尾。 java.util.Map<K,V>集合接口 Map集合的特点:1.Map集合是一个双列集合,一个元素包含两个值(一个Key,一个Value)。 2.Map集合中的元素,key和value的集合数据类型可以相同,可以不同。 3.Map集合中的元素,key是不允许重复的,value是允许重复的。 4.Map集合中的元素,key和value是一一对应的。 java.util.HashMap<k,v>集合 implements Map<k,v>接口 HashMap集合的特点: 1.HashMap集合底层是哈希表:查询速度特别快。JDK1.8之前:数组+单向链表 JDK1.8之后:数组+单向链表/红黑树(链表长度超过8):提高查询效率 2.HashMap集合是一个无序集合,存取元素顺序可能不一致, java.util.LinkedHashMap<k,v>集合 extends HashMap<k,v>集合 LinkedHashMap集合的特点L : 1.LinkedHashMap集合底层是哈希表+链表(保证迭代的顺序) 2.LinkedHashMap集合是一个有序的集合,储存元素和取出元素的顺序是一致的 Map集合的方法: public V put(Key k,V value):把指定的键和指定的值添加到Map集合中。 返回值:V 存储键值对的时候,key不重复,返回值V是null。key重复,会使用新的value替换map中重复的value,返回被替换的value值 public V remove(Object key):把指定的键 所对应的键值元素在Map集合中删除,返回被删除元素的值。 返回值:V key存在,返回被删除的元素,key不存在,返回null public V get(Object key):根据指定的键,在Map集合中获取对应的值。 返回值:key存在返回对应的value;key不存在返回null boolean containsKey(Object key):判断集合中是否包含指定的键。包含返回true,不包含返回false。 Map集合的第一种遍历方式:通过键找值的方式 Map集合中的方法: Set<K> keySet() 返回此映射中包含的键的Set视图。 实现步骤:1.使用Map集合中的方法keySet(),把Map集合中所有的key取出来,储存到一个set集合中 2.遍历set集合,获取Map集合中的每一个key 3.使用迭代器/增强for,通过Map集合中的方法get(key),通过key找value Map.Entry<K,V>:在Map接口中有一个内部接口Entry 作用:当Map集合一创建,那么就会在Map集合中创建一个Entry对象,用来记录键与值(键值对对象,键与值的映射关系)--》结婚证 Set<Map.Entry<K,V>> entrySet()把Map集合内部的多个Entry对象取出来存储到一个Set集合中。 遍历Set集合获取Set集合中的每一个Entry对象。 Entry对象中的方法:getKey()获取key getValue()获取value Set<Map.Entry<String,Integer>> set1=map.entrySet(); for (Map.Entry<String,Integer> entries: set1){ System.out.println(entries.getKey()); System.out.println(entries.getValue()); } Map集合遍历的第二种方式:使用Entry对象遍历: Map集合中的方法:Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射关系的Set视图。 实现步骤: 1.使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中 2.遍历Set集合,获取每一个Entry对象。(用迭代器/增强for循环) 3.使用Entry对象中的方法getKey()和getValue()获取键与值。 HashMap存储自定义类型键值:Map集合保证key是唯一的:作为key的元素,必须重写hashCode方法和equals方法,以保证key唯一。 java.util.HashTable<K,V>集合 implements Map<K,V>接口 Hashtable:底层也是一个哈希表,是一个线程安全的集合,是单线程集合,速度慢。 HashMap:底层是一个哈希表,是一个线程不安全的集合,是多线程集合,速度快。 HashMap集合(之前学的所有集合):可以存储null值,null键 HashTable集合,不能存储null值,null键 Hashtable和Vector集合一样,在jdk1.2版本之后被更先进的集合(HashMap,ArrayList)取代了 HashTable 的子类Properties依然活跃在历史舞台 ; Properties集合是一个唯一和IO流相结合的集合。 JDK9.0的新特性: List接口,Set接口,Map接口:里面增加了一个静态方法of,可以给集合一次性添加多个元素。 static <E> List<E> of (E...elements) 使用前提:当前集合中存储的元素的个数已经确定了,不在改变时使用。 注意: 1.of方法只适用于List接口,Set接口,Map接口,不适用于接口的实现类。 2.of方法的返回值是一个不能改变的集合,集合不能在使用add,put方法添加元素,会抛出异常 UnsupportedOperationException:不支持操作异常 3.Set接口和Map接口在调用of方法的时候,不能有重复的元素,否则会抛出IllegalArgumentException非法参数异常,有重复的元素。 Debug:调试程序: 可以让代码逐行执行,查看代码执行的过程,调试程序中出现的bug 使用方式: 在行号的右边,鼠标左键单击,添加断点(每个方法的第一行,哪里有bug添加到哪里) 右键,选择Debug执行程序。 程序就会停留在添加的第一个断点处 执行程序: f8:逐行执行程序 f7:进入到方法中 shift+f8:跳出方法 f9:跳到下一个断点,如果没有下一个断点,如果没有下一个断点就结束程序。 Ctrl+f2:退出Debug模式,退出程序。 Console:切换到控制台
【【异常】】 java.lang.Throwable:类是 Java语言中所有数据错误或异常的超类。 Exception:编译器异常,进行编译(写代码)java程序出现错误 RuntimeExcepiton:运行期异常,java程序运行过程中出现的问题。 Error:错误 错误就相当于程序得到一个无法治愈的毛病,必须修改源代码程序才能正常执行。 访问数组中的3索引,而数组是没有3索引的,这时候,JVM就会检测程序 会出现异常。 (1)JVM会做两件事: 1.JVM会提供异常产生的原因创建一个异常对象,这个异常对象包含了异常产生的(内容,原因,位置) 2.在方法中,没有异常的处理逻辑(try...catch),那么JVM就会把异常对象抛出给方法的调用者main方法来处理异常。 (2)如果main方法接受到了这个异常对象,main方法也没有异常的处理逻辑,则继续会把对象抛出main方法的调用者JVM处理。 (3)JVM接受到了这个异常对象,做了两件事:1.把异常对象(内容,原因,位置)以红色的字体打印在控制台 2.JVM会终止当前正在执行的java程序--》中断处理 throw关键字: 作用:可以用throw关键字在指定的方法中抛出指定的异常。 使用格式:throw new xxxException("异常产生的原因"); 注意: 1.throw关键字必须写在方法的内部。 2.throw关键字后边new的对象必须是Exception或者Exception的子类对象。 3.throw关键字抛出指定的异常对象,我们就必须处理这个对象。 throw关键字后边创建的是RuntimeException或是 RuntimeException的子类对象,我们可以不处理,默认交给JVM处理(打印异 常对象,中断程序) throw关键字后边创建的是编译异常(写代码的时候报错),我们就必须处理这个异常,要么throws,要么try...catch FileNotFoundException是编译异常,就必须处理这个异常。 Objects类中的静态方法 public static <T> T requireNonNull(T obj):查看指定引用对象不是null。 源码: public static <T> T requireNonNull(T obj){ if(obj==null) thorw new NullPointerException(); return obj; } } throws关键字:异常处理的第一种方式,交给别人处理。 作用:当方法内部抛出异常对象的时候(使用了throw),那么我们就必须处理这个异常对象。 可以使用throws关键字处理异常对象,会把异常对象声明抛出给方法的调用者处理(自己不处理,给别人处理),最终交给JVM处理-->中断处理 使用格式:在方法声明时使用 修饰符 返回值类型 方法名(参数列表) throws AAAException,BBBException...{ throw new AAAException("产生原因"); throw new BBBException("产生原因"); ... } 注意: 1.throws关键字必须写在方法的声明处。 2.throws关键字后面声明的异常必须是Exception或者是Exception的子类。 3.方法内部如果抛出了多个异常对象,那么throws后边必须也声明多个异常。如果抛出的多个异常对象有子父类关系,那么直接声明父类异常即可 4.调用了一个声明抛处异常的方法,我们就必须处理声明的异常,要么继续使用throws声明抛出,交给方法的调用着处理,最终交给JVM;要么用 try...catch自己处理异常。 try...catch:异常处理的第二种方式,自己捕获处理异常。 格式: try{ 可能产生的异常的代码 }catch(异常类名 变量名){ ... } 注意: 1.try可能会抛出多个异常对象,那么就可以使用多个catch来处理这些异常对象 2.如果try中产生了异常,那么就会执行catch中的异常处理逻辑,执行完毕catch中的处理逻辑,继续执行try...catch之后的代码。 如果try中没有产生异常,那么就不会执行catch中异常的处理逻辑,执行完try中的代码,继续执行try...catch之后的代码。 finally代码块 格式: try{ 可能产生异常的代码 }catch(定义一个异常的变量,用来接收try中抛出的异常对象){ 异常处理逻辑,异常对象之后,怎么处理异常对象。 }finally{ 无论是否出现异常都会执行 } 注意:1.finally不能单独使用,必须和try一块使用 2.finally一般用于资源释放(资源回收),无论程序是否出现异常,最后都要资源释放(IO) 当返回值不是基本数据类型的时候,其是指向一段内存的,return将返回段指向一段内存,但是代码段的s依然是指向的同一段内存地址,所以当s修改它指向内存中的值的时候,其实也就修改了返回段指向内存中的值,所以最终的值改变了。 到底返回值变不变可以简单的这么记忆:当finally调用的任何可变API,会修改返回值;当finally调用任何的不可变API,对返回值没有影响。 其实return与finally并没有明显的谁强谁弱。在执行时,是return语句先把返回值写入内存中,然后停下来等待finally语句块执行完,return再执行后面的一段。 如果finally有return语句,永远返回finally中的结果,避免该情况。 Throwable类中定义了3个异常处理的方法(写在catch的{}中) String getMessage() 返回此 throwable的简短描述。 String toString() 返回此 throwable 的详细消息字符串。 void printStackTrace() JVM打印异常对象,默认此方法,打印的异常信息是最全面的。 多个异常使用捕获又该如何处理? 1.多个异常分别处理 2.多个异常一次捕获,多次处理 3.多个异常一次捕获一次处理。 子父类异常: -如果父类方法抛出了多个异常,子类重写父类方法时,抛出和父类相同的异常或者时父类异常的子类或者不抛出异常。 -父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常,此时子类产生该异常,只能捕获处理,不能声明抛出。 注意: 父类异常时什么样,子类异常什么样 自定义类型异常: java提供的异常类,不够我们使用,需要自己定义一些异常。 格式: public class XXXException extends Exception | RuntimeException{ 添加一个空参数的构造方法 public XXXException(){} 添加一个带异常信息的构造方法//查看源码发现,所有的异常都会有一个带异常信息的构造方法,方法内部会调用父类带异常信息的构造方法,让父类来处理这个异常信息。 public XXXException(String message){ super(message);} 注意:1.自定义异常类名字一般都是以Exception结尾,说明该类是一个异常类。 2.自定义异常类,必须继承Exception或者RuntimeException 继承Exception:那么自定义的异常类就是一个编译期异常,如果方法内部抛出了编译期异常,就必须处理这个异常,要么throws,要么try...catch 继承RuntimeException:那么自定义的异常类就是一个运行期异常,无需处理,交给虚拟机处理(中断处理) 并发:指两个或多个事件在同一个时间段内发生。(交替执行) 并行:指两个或多个事件同时刻发生。(同时执行) 硬盘永久储存ROM,内存临时储存RAM 进程:进入到内存中的程序,所有的应用程序都要进入到内存中执行 线成:属于进程,是进程中的一个执行单元,负责程序的执行。 cpu:中央处理器 对数据进行计算,指挥电脑中软件和硬件干活 分类: 单核 多核 8线程:同时执行8个任务 线成调度: 分时调度:所有线程轮流使用CPU的使用权,平均分配给每个线程占用CPU的时间。 抢占式调度:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线成随机性),java使用的为抢占式调度。 单线程程序:java程序中只有一个程序,执行从main方法开始,从上到下依次执行。 JVM执行main方法,main方法会进入到栈内存,JVM会找操作系统开辟一条main方法通向CUP的执行路径,CPU就可以通过这个路径来执行main方法,而这个路径有一个名字叫main(主)线程。 Thread类 构造方法: public Thread():分配一个新的线程对象。 public Thread(String name):分配一个指定名字的新的线程对象。 常用方法: public void run():此线程要执行的任务在此处定义代码。 public static void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。(millis毫秒) 创建多线程程序的第一种方式:创建Thread类的子类 java.lang.Thread类:是描述线程的类,我们想要实现多线程程序,就必须继承thread类 实现步骤: 1.创建一个Thread类的子类 2.在Thread类的子类中重写Thread类中run方法,设置线成任务(开启线成要做什么) 3.创建Thread类的子类对象 4.调用Thread类中的方法start方法,开启新的线程 ,执行run方法 public void start() 使该线程开始执行;java虚拟机调用该线程的run方法,结果是两个线成并发的运行;当前线程(main线成)和另一个线成(创建的新线程,执行其run方法)。多次启动一个线程是非法的,特别是线程已经执行结束后,不能重新启动。 守护线程
JVM执行main方法,打开OS开辟一条main方法通向cpu的路径,这个路径叫main线程,主线程,cup通过这个线程,这个路径可以执行main方法。 对于cpu而言,就有了两条执行的路径,cpu就有了选择的权力,cpu喜欢谁,就会执行哪条路径,我们控制不了cpu,所以就有了程序的随即打印结果, 两个线程抢夺cpu执行权(执行时间)谁抢到了谁执行对应的代码 创建一个线程对象就在内存中开辟了新的栈空间,执行run()方法;多线程的好处:多个线程之间互不应影响(在不同的栈空间) 获取线程的名称: 1.使用Thread类中方法 String getName() 返回该线程的名称。 2.可以先获取当前正在执行的线程,使用线程中的方法getName()获取线程的名称: 使用在Thread类中static Thread currentThread()返回当前正在执行的线程对象的引用。 设置线程的名称:(了解) 1.使用Thread类中的方法setName(名字) void setName(String name):改变线程名称,使之与参数 name 相同 2.创建一个带参数的构造方法,参数传递线程的名称;调用父类的带参构造方法,把线程名称传递给父类,让父类(Thread)给子线程起一个名字。 创建多线程程序的第二种方式:实现Runnable接口 java.lang.Runnable Runnable接口应该由那些打算通过某一线程执行其实例的类来实现,类必须定义一个称为run()无参方法。 java.lang.Thread类的构造方法 Thread(Runnable target): 分配新的Thread对象。 Thread(Runnable target,String name):分配新的对象。 实现步骤: 1.创建一个Runnable接口的实现类 2.在实现类中重写Runnable接口的run方法,设置线程任务。 3.创建一个Runnable接口实现类的对象 4.创建Thread类对象,构造方法中传递Runnable接口的实现类对象。 5.调用Thread类中start方法,开启新的线程执行run方法。 RunnableImpl r=new RunnableImpl(); Thread t=new Thread(r); t.start(); 实现Runnable接口创建多线程程序的好处: 1.避免单继承的局限性。 一个类只能有一个父类,类继承了Thread类就不能继承其他的类。如果实现Runnable接口,还可以继承其他的类或接口。 2.增强了程序的扩展性,降低了程序的耦合性(解耦) 实现Runnable接口的方式,把设置线程任务和开启新线程进行了分离(解耦),实现类中,重写了run方法:用来设置线程任务。创建Thread类对象,调用start方法:用来开启新线程。 匿名内部类方式实现线程的创建。 匿名:没有名字 内部类:写在其他类内部的类 匿名内部类的作用:简化代码 把子类继承父类,重写父类的方法,创建子类对象合成一步完成。 把实现类 实现接口,重写接口中的方法,创建实现类对象一步完成。 匿名内部类的最终产物:子类/实现类对象,而这个类没有名字。 格式: new 父类/接口(){} new Thread() { @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println("黑马"+i); } } }.start(); Runnable r=new Runnable() { @Override public void run(){ for (int i = 0; i < 20; i++) { System.out.println("传智播客"+i); } } }; new Thread(r).start(); 单线程程序是不会出现线程安全问题的;多线程程序,没有访问共享数据,不会产生问题;多线程访问了共享的数据,会产生线程安全问题。 线程安全问题是不能产生的,我们可以让一个线程在访问共享数据的时候,无论是否失去了cpu的执行权,其他的线程只能等待,等待当前线程用完共享数据,其他线程才能访问共享资源。 解决线程安全问题的一种方案:使用同步代码块 格式: synchronized(锁对象){ 可能会出现线程安全问题的代码(访问了共享数据的代码) } 注意: 1.通过代码块中的锁对象,可以使用任意的对象。 2.但必须保证多个线程使用的锁对象是同一个 3.锁对象的作用:把同步代码块锁住,只让一个线程在同步代码块中执行。 同步技术的原理:使用了一个锁对象叫同步锁,也叫对象锁,也叫对象监视器。同步中的线程没有执行完毕不会释放锁,同步外的线程没有锁进不去同步(进入阻塞状态)。 同步保证了只能有一个线程在同步中执行共享数据,保证了安全,程序频繁的判断锁,获取锁,释放锁,程序的效率会降低。 解决线程安全问题的第二种方案:使用同步方法: 使用步骤: 1.把访问了共享数据的代码抽取出来,放到一个方法中 2.在方法上添加synchronized修饰符 格式:定义方法的格式 修饰符 synchronized 返回值类型 方法名(参数列表){ 可能会出现线程安全问题的代码(访问共享数据的代码) } 定义一个同步方法:同步方法也会把方法内部的代码锁住,只让一个线程执行;同步方法的锁对象就是 实现类对象,也就是this 静态的同步方法:锁对象不能是this,this是创建对象之后产生的,静态方法优先于对象;静态方法的锁对象是本类的class属性--》class文件对象(反射) 方法被static修饰,synchronized在方法中或者方法中有synchronized同步代码块。 解决线程安全问题的第三种方案:使用lock锁 java.util.concurrent.locks.lock接口 Lock的实现类提供了比使用 synchronized方法和语句可获得的更广泛的锁定操作。 Lock接口中的方法: void lock() 获取锁 void unlock 释放锁 java.util.concurrent.locks.Reentrantlock implements lock接口 使用步骤: 1.在成员位置创建一个Reentrantlock对象 2.在可能会出现安全问题的代码前调用lock接口中的方法lock获取锁。 3.在可能会出现安全问题的代码后调用lock接口中的方法unlock释放锁(尽量写在finally{}里面) 线程六种状态: 新建状态 NEW至今尚未启动的线程处于这种状态。 运行状态 RUNNABLE 正在java虚拟机中执行的线程处于这种状态 阻塞状态 BLOCKED受阻塞并等待某个监视器锁的线程处于这种状态。 无限(永久)等待状态 WAITING 无线期地等待另一个线程来执行某一特定操作的线程处于这种状态。 休眠(睡眠)状态 TIMED_WAITING 等待另一个线程来执行,取决于指定等待时间的操作的 线程处于这种状态, 死亡状态 TERMINATED 已经退出的线程处于这种状态。 阻塞状态:具有cpu的执行资格,等待cpu空闲时执行; 休眠状态:放弃cpu的执行资格,cpu空闲,也不执行
Lambda表达式函数式编程思路 面向对象的思想:做一件事情,找一个能解决这个事情的对象,调用对象的方法,完成事情。 函数式编程思想:只要能获取到结果,谁去做,怎么做的都不重要;重视的是结果,不重视过程。
new Thread(new Runnable(){ public void run(){ System.out.println(Thread.currentThread().getName()); } }).start(); 借助Java8的全新语法,上述Runnable接口的匿名内部类写法可以通过更简单的Lambda表达式达到等效: new Thread( ()->{ System.out.println(Thread.currentThread().getName()); } ).start(); Lambda表达式的标准格式(无参无返回值): 由三部分组成: a.一些参数 b.一个箭头 c.一段代码 格式: (参数列表) -> {一些重写方法的代码}; 解释说明格式: ():接口中抽象方法的参数列表,没有参数,就空着;有参数就写出参数,多个参数使用逗号分隔; ->:传递的意思,把参数传递给方法体; {}:重写接口的抽象方法的方法体有参有返回值:
/* Arrays.sort(arr, new Comparator<Person>() { @Override public int compare(Person o1, Person o2) { return o1.getAge()-o2.getAge(); } });*/ Arrays.sort(arr,(o1,o2)->{ return o1.getAge()-o2.getAge(); });
public static void main(String[] args) { invockCal(20, 30, new Calculator() { @Override public int add(int a, int b) { return a+b; } }); invockCal(30,50,(a,b)->{ return a+b; }); } public static void invockCal(int a,int b,Calculator c){ System.out.println(c.add(a,b)); } |
java.io.File类 :文件和目录路径名的抽象表示形式。 java把电脑中的文件和文件夹(目录)封装成为一个File类,我们可以使用File类对文件和文件夹进行操作 我们可以使用File类的方法: 创建一个文件/文件夹 删除文件/文件夹 获取文件/文件夹 判断文件/文件夹是否存在 对文件夹进行遍历 获取文件的大小 File类是一个与系统无关的类,任何操作系统都可以使用这个类中的方法 File:文件 directory:文件夹/目录 path:路径
static String |
pathSeparator 与系统有关的路径分隔符,为了方便,它被表示为一个字符串。 |
static char |
pathSeparatorChar 与系统有关的路径分隔符。 |
static String |
separator 与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串。 |
static char |
separatorChar 与系统有关的默认名称分隔符。 |
File(String pathname)
通过将给定路径名字符串转换为抽象路径名来创建一个新 File
实例。
参数:
String pathname:字符串的路径名称
路径可以是以文件结尾,也可以是以文件夹结尾;路径可以是相对路径也可以是绝对路径
路径可以是存在的也可以是不存在的;创建File对象,只是把字符串路径封装为File对象,不考虑路径的真假情况。
File(String parent, String child)
根据 parent 路径名字符串和 child 路径名字符串创建一个新
File
实例。
参数: 把路径拆分成两个部分
String parent:父路径
String child:子路径
好处:父路径和子路径单独书写,使用起来非常灵活;父路径和子路径都可以变化
File(File parent, String child)
根据 parent 抽象路径名和 child 路径名字符串创建一个新
File
实例。
参数: 把路径拆分成两个部分
File parent:父路径
String child:子路径
好处: 父路径和子路径,可以单独书写,使用起来非常灵活,父路径和子路径都可以变化
File(URI uri)
通过将给定的 file: URI 转换为一个抽象路径名来创建一个新的 File 实例。 方法: public String getAbsolutePath() :返回此File的绝对路径名字符串。 获取的构造方法中传递的路径。 无论路径是绝对路径还是相对路径,getAbsolutePath方法返回的都是绝对路径。
public String getPath()
- 将此抽象路径名转换为一个路径名字符串。所得字符串使用
默认名称分隔符
分隔名称序列中的名称。
-
返回:此抽象路径名的字符串形式
toString方法调用的就是getPath方法 源码:public string tostring(){ return getPath(); }
public String getName():返回由此抽象路径名表示的文件或目录的名称。该名称是路径名名称序列中的最后一个名称。如果路径名名称序列为空 则返回空字符串。
-
返回: 此抽象路径名表示的文件或目录的名称;如果路径名的名称序列为空,则返回空字符串
public long length()
- 返回由此抽象路径名表示的文件的长度。注意文件夹没有大小的概念,不能获取文件夹的大小。如果构造方法给出的路径不存在,那么leng方法返回0
- 返回:
- 此抽象路径名表示的文件的长度,以字节为单位;如果文件不存在,则返回
0L
。对于表示特定于系统的实体(比如设备或管道)的路径名,某些操作系统可能返回0 L
。
public boolean createNewFile() throws IOException createNewFile声明抛出了IOException。我们调用这个方法,就必须处理这个异常,要么使用throws,要么使用try...catch public boolean mkdir(): 创建单级文件夹 public boolean mkdirs(): 既可以创建单级空文件夹,也可以创建多级文件夹,创建文件夹的路径和名称在构造方法中给出(构造方法的参数) 返回值:布尔值 true: 文件夹不存在,创建文件夹,返回true false:文件夹存在,不会创建,返回false;构造方法中给出的路径不存在返回false 注意:此方法只能创建文件夹,不能创建文件 public boolean delete():删除由此File表示的文件或目录。此方法可以删除构造方法路径中给出的文件/文件夹 返回值:布尔值 文件/文件夹删除成功,返回true; 文件夹中有内容,不能删除返回false; 构造方法中路径不存在返回false delete方法时直接在硬盘删除文件/文件夹,不能走回收站,删除要谨慎。 file类遍历(文件夹)目录功能 public String[] list():返回一个String数组,表示该File目录中的所有子文件或目录。 遍历构造方法给出的目录,会获取目录中所有文件/文件夹的名称,把获取到的多个名称储存到一个String类型的数组中。 public File[] listFiles():返回一个File数组,表示该File目录中的所有的子文件或目录。 遍历构造方法给出的目录,会获取目录中所有文件/文件夹,把文件/文件夹封装为File对象,多个File对象存储到File数组中。 注意: list方法和listFiles方法遍历的时构造方法中给出目录。如果构造方法中给出的目录的路径不存在,会抛出空指针异常; 如果构造方法中给出的路径不是一个目录,也会抛出空指针异常。 recursion 递归的分类: -递归分为两种,直接递归和间接递归。直接递归称为方法自身调用自身;间接递归可以A方法调用B方法,B方法调用C方法,C方法调用A方法。 注意事项: 递归一定要有条件限定,保证递归能够停止下来,否则会发生栈内存溢出。 在递归中虽然有限定条件,但是递归次数不能太多,否则也会发生栈内存溢出。 构造方法,禁止递归,编译错误:构造方法是创建对象使用的,一直递归会导致内存中有无数多个对象,直接编译报错。 递归的使用前提:当调用方法的时候,方法的主体不变,每次调用方法的参数不同,可以使用递归。 注意:当一个方法调用其他方法的时候,被调用的方法没有执行完毕,当前方法会一直等待调用的方法执行完毕,才会继续执行。 使用递归求和,main方法调用sum方法,sum方法会一直调用sum递归,导致在内存中有多个sum方法(频繁的创建方法,调用发放,销毁方法)效率低 使用递归必须明确: 1.递归的结束条件 2.递归的目的
//递归打印多级目录 public static void getAllFile(File dir){ System.out.println(dir);//打印被遍历的目录名称 File[] files=dir.listFiles(); for (File d:files) { //对遍历得到的File对象d进行判断,判断是否是文件夹 if(d.isDirectory()){ //d是一个文件夹,则继续遍历这个文件夹。 //我们发现getAllFile方法就是传递文件夹,遍历文件夹的方法。 //所以直接使用getAllFile方法即可:递归(自己调用自己) getAllFile(d); }else{ System.out.println(d); } } } 在File类中有两个和ListFiles重载的方法,方法的参数传递的就是过滤器。 File[] listFiles(FileFilter filter) java.io.FileFilter 接口:用于抽象路径名(File对象)的过滤器。 作用:用来过滤文件(File对象) 抽象方法:用来过滤文件的方法 boolean accept(File pathname):测试只当抽象路径名是否应该包含在某个路径名列表中。 参数: File pathname:使用ListFiles方法遍历目录,得到的每一个文件对象 File[] listFiles(FilenameFilter filter) java.io.FilenameFilter接口:实现此接口的类实例可用于过滤器文件名。 作用:用于过滤文件名称 抽象方法:用于过滤文件的方法 boolean accept(File dir,String name):测试指定文件是否应该包含在某一文件列表中。 参数: File dir:File构造方法中传递的被遍历的目录 String name:使用ListFiles方法遍历目录,获取的每一个文件/文件夹的名称 注意:两个过滤器接口是没有实现类的,需要我们自己写实现类,重写过滤的方法accept,在方法中自己定义过滤的规则。 必须明确两件事:1.过滤器中的accept方法是谁调用的 2.accept方法参数pathname是什么? accept方法返回值是一个布尔值;true:就会把传递过去的File对象保存到File数组中;false:就不会把传递过去的File对象保存到File数组中 listFiles方法一共做了3件事: 1.listFiles方法对构造方法中传递的目录进行遍历,获取目录中每一个文件/文件夹--》封装成File对象 2.listFiles方法会调用参数传递的过滤器中的方法accept 3.listFiles方法会把遍历得到的每一个File对象,传递给accept方法的参数pathname
【【流】】 java.io.OutputStream:字节输出流 此抽象类是表示输出字节流的所有类的超类。 定义了一些子类共性的成员方法: -public void close():关闭此输出流并释放与此流相关联的任何系统资源。 -public void flush():刷新此输出流并强制任何缓冲的输出字节被写出。 -public void write(byte[] b):将b.length字节从指定的字节数组写入此输出流。 一次写多个字节是正数(0~127),那么显示的时候会查询ASCII表 如果第一个字节是负数,那么第一个字节会和第二个字节,两个字节组成一个中文显示,查询系统默认码表。 -public void write(byte[] b,int off,int len):从指定的字节数组写入len字节,从偏移量off开始输出到此输出流。 -public abstract void write(int b):将指定的字节输出流。 java.io.FileOutputStream extends OutputStream FileOutputStream:文件字节输出流 作用:把内存中的数据写入到硬盘的文件中。 构造方法: FileOutputStream(String name):创造一个向具有指定路径名称的文件中写入数据的输出文件流。 FileOutputStream(File file):创建一个向指定File对象表示的文件中写入数据的文件输出流。 参数:写入数据的目的 String name:目的地是一个文件的路径。 File file:目的地是一个文件。 构造方法的作用: 1.创建一个FileOutputStream对象 2.会根据构造方法中传递的文件/文件路径,创建一个空的文件。 3.会把FileOutputStream对象指向创建好的文件。 写入数据的原理(内存->硬盘) java程序--》JVM(java虚拟机)--》OS(操作系统)--》OS调用写数据的方法--》把数据写到文件中 字节输出流的使用步骤: 1.创建一个FileOutputStream对象,构造方法中传递写入数据的目的地。 2.调用FileOutputStream对象中的方法write,把数据写入到文件中。 3.释放资源(流使用会占用一定的内存,使用完毕要把内存清空,提高程序的效率)
FileNotFoundException异常是IOException异常的子类。 写数据的时候,会把10进制的整数转化成二进制的;硬盘中存储的数据都是字节 1个字节=8个比特位 任意的文本编辑器(记事本,notepad++)在打开文件的时候,都会查询编码表,把字节转化为字符表示 0~127:查询ASCII表 97-》a 其他值:查询系统默认码表(中文系统GBK) 在文件中写100 写三个字节 UTF-8中三个字节是一个中文 GBK中两个字节表示一个中文 写入字符的方法:可以使用String类中的方法把字符串转换为字节数组 byte[] getBytes() 把字符串转换为字节数组 追加写/续写:使用两个参数的构造方法 FileOutputStream(String name,boolean append):创建一个向具有指定name的文件中写入数据的输出文件流。 FileOutputStream(File file,boolean append):创建一个向指定File对象表示的文件中写入数据的文件输出流。 参数: String name,File file:写入数据的目的地 boolean append:追加写开关 true:创建对象不会覆盖原文件,继续在文件的末尾追加写数据。 false:创建一个新文件,覆盖原文件。 写换行:写换行符号 windows:\r\n linux:/n mac:/r java.io.InputStream:字节输入流 此抽象类是表示字节输入流的所有类的超类。 定义了所有子类共性的方法: int read() 从输入流中读取数据的下一个字节。读到文件的末尾返回-1。 int read(byte[] b):从输入流中读取一定数量的字节,并将其储存在缓冲区数组b中。 void close() 关闭此输入流并释放与该流相关联的所有系统资源。 java.io.FileInputStream extends InputStream FileInputStream:文件字节输入流 作用:把硬盘文件中的数据,读取到内存中使用。 构造方法: FileInputStream(String name) FileInputStream(File file) 参数:读取文件的数据源 String name:文件的路径 File file : 文件 构造方法的作用: 1. 会创建一个FileInputStream对象 2.会把FileInputStream对象指向构造方法中要读取的文件。 读取数据的原理(硬盘-->内存) java程序-->JVM-->OS-->OS读取数据的方法-->读取文件 字节输入流的使用步骤: 1.创建FileInputStream对象,构造方法中绑定要读取的数据源。 2.使用FileInputStream对象中的方法read,读取文件 3.释放资源。
int len=0;//记录读取到的字节 while ((len=fis.read())!=-1){ System.out.println((char)len); } |
字节输入流一次读取多个字节的方法: int read(byte[] b) 从输入流中读取到一定数量的字节,并将其储存在缓冲区数组b中。 明确两件事情: 1.方法的参数byte[]的作用? 起到缓冲作用,储存每次读取到的多个字节。数组的长度一般定义为1024(1kb)或者1024的整倍数 2.方法的返回值int是每次读取的有效字节个数。 String(byte[] bytes):把字节数组转化为字符串 String(byte[] bytes,int offset,int length):把字节数组的一部分转化为字符串 offset:数组的开始索引 length:转换的字节个数。
文件复制的步骤: 1.创建一个字节输入流对象,构造方法中绑定要读取的数据源 2.创建一个字节输入流对象,构造方法中绑定要写入的目的地。 3.使用字节输入流对象中的方法read读取文件。 4.使用字节输出流中方法write,把读取到的字节写入到目的地的文件中。 5.释放资源。 FileInputStream fis = new FileInputStream("C:\\IdeaProjects\\com.itheima\\b.txt"); FileOutputStream fos = new FileOutputStream("C:\\IdeaProjects\\com.itheima\\c.txt"); byte[] b=new byte[1024]; int len = 0; while ((len = fis.read(b)) != -1) { fos.write(b,0,len);//读多少写多少 } //释放资源,(先关闭写的,再关闭读的,如果写完了,肯定读完了) fos.close(); fis.close(); |
java.io.Reader:字符输入流,是字符输入流的最顶层的父类,定义了一些共性的成员方法,是一个抽象类 共性的成员方法: int read() 读取单个字符并返回 int read(char[] cbuf):一次读取多个字符,将字符读入数组。 void close() 关闭该流并释放与之关联的所有资源。
java.io.FileReader extends InputSreamReader extends Reader
FileReader:文件字符输入流。 作用:把硬盘文件中的数据以字符的方式读取到内存中。 构造方法: FileReader(String fileName) 文件的路径 FileReader(File file) 一个文件 参数:读取文件的数据源 FileReader构造方法的作用: 1.创建一个FilReader对象 2.会把FileReader对象指向要读取的文件 字符输入流的使用步骤:
1.创建FileReader对象,构造方法中绑定要读取的数据源 2.使用FileReader对象中的方法read读取去的数据源 3.释放资源。 java.io.Writer:字符输出流,是所有字符输出流的最顶层的父类,是一个抽象类。 共性的成员方法:
Writer |
append(char c) 将指定字符添加到此 writer。 |
Writer |
append(CharSequence csq) 将指定字符序列添加到此 writer。 |
Writer |
append(CharSequence csq, int start, int end) 将指定字符序列的子序列添加到此 writer.Appendable。 |
abstract void |
|
abstract void |
flush() 刷新该流的缓冲。 |
void |
write(char[] cbuf) 写入字符数组。 |
abstract void |
write(char[] cbuf, int off, int len) 写入字符数组的某一部分。 |
void |
write(int c) 写入单个字符。 |
void |
write(String str) 写入字符串。 |
void |
write(String str, int off, int len) 写入字符串的某一部分。 |
FileWriter(File file)
根据给定的 File 对象构造一个 FileWriter 对象。
FileWriter(String fileName)
根据给定的文件名构造一个 FileWriter 对象。
构造方法的作用:
1.创造FileWriter对象。
2.会根据构造方法中传递的文件/文件的路径,创建文件。
3.会把FileWriter对象指向创建好的文件。
字符输出流的使用步骤:
1.创建FileWriter对象,构造方法中绑定要写入数据的目的地
2.使用FileWriter中的方法writer,把数据写入到内存缓冲区中(字符转换为字节的过程)
3.使用FileWriter中的方法flush。把内存缓冲区中的数据,刷新到文件中。
4.释放资源(会先把内存缓冲区中的数据刷新到文件中)
flush方法和close方法的区别
-flush:刷新缓冲区,流对象可以继续使用
-close:先刷新缓冲区,然后通知系统释放资源,流对象不可以再被使用了
续写和换行
续写:追加写:使用两个参数的构造方法
FileWriter(String fileName,boolean append)
FileWriter(File file,boolean append)
参数:String name,File file:写入数据的目的地
boolean append:追加写开关
true:创建对象不会覆盖原文件,继续在文件的末尾追加写数据。
false:创建一个新文件,覆盖原文件。
写换行:写换行符号
windows:\r\n
linux:/n
mac:/r
再jdk1.7之前使用try catch finally 处理流中的异常 格式: try{ 可能会产出异常的代码 }catch(异常类变量 变量名){ 异常的处理逻辑 }finally{ 一定会执行的代码 资源释放 }
//提高变量fw的作用域,让finally可以使用 //变量在定义的时候,可以没有值,但是使用的时候必须有值 FileWriter fw = null; try { //可能会产出异常的代码 fw = new FileWriter("C:\\IdeaProjects\\com.itheima\\c.txt", true); for (int i = 0; i < 10; i++) { fw.write("HelloWorld" + i + "\r\n"); } } catch (IOException e) { //异常处理逻辑 System.out.println(e); } finally { //一定会执行的代码 //创建对象失败了,fw的默认值为null,null是不能调用方法的,会抛出NullPointerException,需要增加一个判断,不是null把资源释放 if (fw != null) { try { fw.close(); } catch (IOException e) { e.printStackTrace(); } } } |
//不用关流 try ( FileInputStream fis = new FileInputStream("C:\\IdeaProjects\\com.itheima\\b.txt"); FileOutputStream fos = new FileOutputStream("C:\\IdeaProjects\\com.itheima\\c.txt"); ) { byte[] b = new byte[1024]; int len = 0; while ((len = fis.read(b)) != -1) { fos.write(b, 0, len); } } catch (IOException e) { System.out.println(e); } |
//自动关流 public static void main(String[] args) throws FileNotFoundException { FileInputStream fis = new FileInputStream("C:\\IdeaProjects\\com.itheima\\b.txt"); FileOutputStream fos = new FileOutputStream("C:\\IdeaProjects\\com.itheima\\c.txt"); try (fis; fos) { byte[] b = new byte[1024]; int len = 0; while ((len = fis.read(b)) != -1) { fos.write(b, 0, len); } } catch (IOException e) { System.out.println(e); } } } |
setProperty(String key, String value)
调用 Hashtable 的方法 put
。
getProperty(String key)
用指定的键在此属性列表中搜索属
stringPropertyNames()
返回此属性列表中的键集,其中该键及其对应值是字符串,如果在主属性列表中未找到同名的键,则还包括默认属性列 表中不同的键。
可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储。
void store(OutputStream out,String comments)
void store(Writer writer,String comments)
参数:
OutputStream out:字节输出流,不能写入中文。
Writer writer:字符输出流,可以写中文。
String comment:注释,用来解释说明保存的文件是做什么用的,不能使用中文,会产生乱码,默认是Unicode编码,一般使空“”字符串
使用步骤:
1.创建Properties集合对象,添加数据。
2.创建字节输出流/字符输出流对象,构造方法中绑定要输出的目的地。
3.使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中储存。
4.释放资源。匿名创建的字节输出流/字符输出流对象会自动关闭,不用close
可以使用Properties集合中的方法load,把硬件中保存的文件(键值对),读取到集合中使用
void load(InputStream inStream)
void load(Reader reader)
参数:
InputStream inStream :字节输入流,不能读取含有中文的键值对
Reader reader :字符输出流,能读取含有中文的键值对
使用步骤:
1.创建Properties集合对象
2.使用Properties集合对象中的方法load读取保存键值对的文件
3.遍历Properties集合
注意:
1.储存键值对的文件中,键与值默认的连接符号可以使用-,空格(其他符号)
2.储存键值对的文件中,可以使用#进行注释,备注是的键值对不会再被读取。
3.储存键值对的文件中,键与值默认都是字符串,不用再加引号。
字节缓冲输入流 给基本的字节输入流增加一个缓冲区(数组)提高基本的字节输入流的读写效率 java.io.BufferedOutputStream extends OutputStream BufferedOutputStream:字节缓冲输出流 继承自父类的共性成员方法: -public void close():关闭此输出流并释放与此流相关联的任何系统资源。
-public void flush():刷新此输出流并强制任何缓冲的输出字节被写出。
-public void write(byte[] b):将b.length字节从指定的字节数组写入此输出流。
一次写多个字节是正数(0~127),那么显示的时候会查询ASCII表
如果第一个字节是负数,那么第一个字节会和第二个字节,两个字节组成一个中文显示,查询系统默认码表。
-public void write(byte[] b,int off,int len):从指定的字节数组写入len字节,从偏移量off开始输出到此输出流。
-public abstract void write(int b):将指定的字节输出流。 构造方法:
BufferedOutputStream(OutputStream out)
: 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
BufferedOutputStream(OutputStream out, int size):
创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流。
参数:
OutputStream out:字节输出流
我们可以传递FileOutputStream,缓冲流会给FileOutputStream增加一个缓冲区,提高FileOutputStream的写入效率。
int size:指定缓冲流内部缓冲区的大小,不指定默认
使用步骤:
1.创建FileOutputStream对象,构造方法中绑定要输出的目的地。
2.创建BufferedOutputStream对象,构造方法中传递FileOutputStream对象,提高FileOutputStream对象效率。
3.使用BufferedOutputStream对象中的方法write,把数据写入到内部缓冲区中。
4.使用BufferedOutputStream对象中的方法flush,把内部缓冲区中的数据,刷新到文件中。
5.释放资源(会先调用flush方法刷新数据,第四步可以省略)
public class BufferedInputStream extends FilterInputStream
BufferedInputStream:字节缓冲输入流
继承自父类的成员方法:
int read():从输入流中读取数据的下一个字节。
int read(char[] cbuf):一次读取多个字符,将字符读入数组。
void close() 关闭该流并释放与之关联的所有资源。
构造方法:
BufferedInputStream(InputStream in)
创建一个 BufferedInputStream
并保存其参数,即输入流 in
,以便将来使用。
BufferedInputStream(InputStream in, int size)
创建具有指定缓冲区大小的 BufferedInputStream
并保存其参数,即输入流 in
,以 便将来使用。
参数:
InputStream in:字节输入流
我们可以传递FileInputStream,缓冲流会给FileInputStream增加一个缓冲区,提高FileInputStream的读写效率。
int size :指定缓冲流内部缓冲区的大小,不指定默认。
使用步骤:
1.创建FileInputStream对象,构造方法中绑定要读取的数据源。
2.创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象的读写效率。
3.使用BufferedInputStream对象中的方法read,读取文件。
4.释放资源。
java.io.BufferedWriter extends Writer
BufferedWriter :字符缓冲输出流
继承自父类的共性成员方法:
void |
close() 关闭此流,但要先刷新它。 |
void |
flush() 刷新该流的缓冲。 |
void |
newLine() 写入一个行分隔符。 |
void |
write(char[] cbuf, int off, int len) 写入字符数组的某一部分。 |
void |
write(int c) 写入单个字符。 |
void |
write(String s, int off, int len) 写入字符串的某一部分。 |
BufferedWriter(Writer out) 创建一个使用默认大小输出缓冲区的缓冲字符输出流。 |
BufferedWriter(Writer out, int sz) 创建一个使用给定大小输出缓冲区的新缓冲字符输出流。 |
void |
newLine() 写入一个行分隔符。 |
BufferedReader(Reader in)
创建一个使用默认大小输入缓冲区的缓冲字符输入流。
BufferedReader(Reader in, int sz)
创建一个使用指定大小输入缓冲区的缓冲字符输入流。
参数:
Reader in:字符输入流
我们可以传递FileReader,缓冲流会给FileReader增加一个缓冲区,提高FileReader的读取效率。
特有的成员方法:
String readLine() 读取一个文本行。读取一行数据。while循环遍历,读取到null结束,返回null
行的终止符号:通过下列字符之一即可认为行已禁止;换行('\n')、回车('\r')或回车后直接跟着换行(\r\n)。
返回值:
包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回null
使用步骤:
1.创建字符缓冲输入流对象,构造方法中传递字符输入流。
2.使用字符缓冲输入流对象中的方法read/readLine读取文本
3.释放资源。
字符编码:就是一套自然语言的字符与二进制之间的对应规则。
编码表:生活中文字和计算机中二进制的对应规则。
编码: 字符(能看懂)——》字节(看不懂的)
解码: 字节(看不懂的)——》字符(能看懂的)
FileReader读取项目中的文本文件时,由于IDEA的设置,都是默认的UTF-8编码,Window系统默认的都是GBK编码,所以会出现乱码。
java.io.OutputStreamwriter extends Writer
OutputStreamWriter:是字符通向字节的桥梁,可以使用指定的charset,将要写入流中的字符编码成字节。
继承自父类的共性成员方法:
void |
close() 关闭此流,但要先刷新它。 |
void |
flush() 刷新该流的缓冲。 |
String |
getEncoding() 返回此流使用的字符编码的名称。 |
void |
write(char[] cbuf, int off, int len) 写入字符数组的某一部分。 |
void |
write(int c) 写入单个字符。 |
void |
write(String str, int off, int len) 写入字符串的某一部分。 |
void |
close() 关闭该流并释放与之关联的所有资源。 |
String |
getEncoding() 返回此流使用的字符编码的名称。 |
int |
read() 读取单个字符。 |
int |
read(char[] cbuf, int offset, int length) 将字符读入数组中的某一部分。 |
boolean |
ready() 判断此流是否已经准备好用于读取。 |
InputStreamReader(InputStream in) 创建一个使用默认字符集的 InputStreamReader。 |
InputStreamReader(InputStream in, Charset cs) 创建使用给定字符集的 InputStreamReader。 |
PrintStream(File file)
创建具有指定文件且不带自动行刷新的新打印流。输出的目的地是一个文件。
PrintStream(OutputStream out)
创建新的打印流。输出目的地是一个字节输出流。
PrintStream(String fileName)
创建具有指定文件名称且不带自动行刷新的新打印流。输出的目的地是一个文件路径。
PrintStream extends OutputStream
继承自父类的成员方法:
-public void close():关闭此输出流并释放与此流相关联的任何系统资源。-public void flush():刷新此输出流并强制任何缓冲的输出字节被写出。
-public void write(byte[] b):将b.length字节从指定的字节数组写入此输出流。
一次写多个字节是正数(0~127),那么显示的时候会查询ASCII表
如果第一个字节是负数,那么第一个字节会和第二个字节,两个字节组成一个中文显示,查询系统默认码表。
-public void write(byte[] b,int off,int len):从指定的字节数组写入len字节,从偏移量off开始输出到此输出流。
-public abstract void write(int b):将指定的字节输出流。 注意: 如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表:97->a 如果使用自己特有的方法print/println方法写数据,写的数据原样输出 97->97 可以改变输出语句的目的地(打印流的语句) 输出语句,默认在控制台输出 使用System.setOut方法改变输出语句的目的地改为参数中传递的打印流的目的地。 static void setOut(PrintStream out) 重新分配“标准”输出流。
public static void main(String[] args) throws FileNotFoundException { System.out.println("在控制台打印输出"); PrintStream ps=new PrintStream("C:\\IdeaProjects\\com.itheima\\p.txt"); System.setOut(ps); System.out.println("在打印流目的地打印"); } |
【【网络】】 物理层/数据链路层:链路层是用于定义物理传输通道,通常是对某些网络连接设备的驱动协议,例如针对光纤、网线提供的驱动。 网络层:网络层是整个TCP/IP协议的核心,它主要用于将传输的数据进行分组,将分组数据发送到目标计算机或者网络。 运输层:主要使网络程序进行通信,在进行网络通信时,可以采用TCP协议,也可以采用UDP协议。 应用层:主要负责应用程序的协议,例如HTTP协议,FTP协议等。 协议 UDP:用户数据报协议(User Datagram Protocol),面向无连接性,不能保证数据的完整性。特点:数据被限制在64kb以内,超出这个范围就不能 发送了。比如视频会议。 TCP:传输控制协议(Transmission Control Protocol),得到的TCP协议面向连接的通信协议。提供了两个计算机之间可靠无差错的数据传输。 IP地址。 三次握手: 第一次握手,客户端向服务端发送连接请求,等待服务端确认。 第二次握手,服务器向客户端回送一个响应,通知客户端收到了连接请求。 第三次握手,客户端再次向服务器端发送确认信息,确认连接。 IPv4:是一个32位的二进制数,通常被分为4个字节,表示成a、b、c、d的形式,例如192.168.65.100。其中a、b、c、d都是0-255之间的十进制整数,那么最多可以表示2的32次方个大约42亿个。 IPv6:采用128位地址长度,每16个字节一组,分成8组十六进制数。 常用命令: 查看本地IP地址,在控制台输入: ipconfig 检查网络是否连通,在控制台输入:ping 空格 IP地址 特殊的IP地址: 本机IP地址: 127.0.0.1 、 localhost 端口号:是一个逻辑端口,我们无法直接看到,可以使用一些软件查看端口号。 当我们使用网络软件一打开,那么操作系统就会为网络软件分配一些软件查看端口号 或者网络软件在打开的时候和系统要 指定的端口号 端口号是由两个字节组成,取值范围在0-65535之间 注意: 1024之前的端口号已经被系统分配给已知的网络软件,我们不能用。 网络软件的端口号不能重复。 常用的端口号: 1.80端口 网络端口 www.baidu.com:80正确的网址 2.数据库 mysql:3306 oracle:1521 3.Tomcat服务区:8080 多个客服端同时和服务器交互,就需要使用多个IO流对象。服务器是没有IO流的,服务器可以获取到请求的客户端对象Socket使用每个客户端Socket中提供的IO流和客户端进行交互。服务器用客户端的字节输入流和字节输出流给客户端发送数据和回写数据。 TCP通信的客户端:向服务器发送连接请求,给服务器发送数据,读取服务器回写的数据。 表示客户端的类: java.net.Socket:此类实现客户端套接字(也可以叫“套接字”),套接字是两台机器间通信的端点。 套接字:包含了IP地址和端口号的网络单位。 构造方法:
Socket(InetAddress address, int port) :
创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
参数:
String host:服务器主机的名称/服务器的IP地址。
int port:服务器的端口号。
成员方法:
getOutputStream()
返回此套接字的输出流。
getInputStream()
返回此套接字的输入流。
close()
关闭此套接字。
实现步骤:
1.创建一个客户端对象Socket,构造方法绑定服务器的IP地址和端口号。
2.使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象。
3.使用网络字节输出流OutputStream对象中的方法write(),给服务器发送数据。
4.使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象。
5.使用网络字节输入流InputStream对象中的方法read,读取服务器回写的数据。
注意:
1.客户端和服务器端进行交互,必须使用Socket中提供的网络流,不能使用自己创建的流对象。
2.当我们创建客户端对象Socket的时候,就会去请求服务器和服务器经过3次握手建立连接通络路。
这时如果服务器没有启动,就会抛异常;如果服务器已经启动,那么就可以进行交互了。
TCP通信的服务端:接收客户端的请求,读取客户端发送的数据。给客户端回写数据表示服务端的类:
java.net.ServerSocket:此类实现服务器套接字。
构造方法:
ServerSocket(int port)创建绑定到特定端口的服务器套接字。
服务器端必须明确一件事情,必须知道是哪个客户端请求的服务器。
所以可以使用accept方法获取到请求的客户端对象Socket
成员方法:
Socket accept() 监听并接受到此套字节的连接。
服务器的实现步骤:
1.创建服务器ServerSocket对象和系统要指定的端口号。
2.使用ServerSocket对象中的方法accept。获取到请求的客户端对象Socket。
3.使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象。
4.使用网络字节输出流OutputStream对象中的方法write(),给服务器发送数据。
5.使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象。
6.使用网络字节输入流InputStream对象中的方法read,读取服务器回写的数据。
7.释放资源(Socket、ServerSocket)
客户端和服务器和本地硬盘进行读写,需要使用自己创建的字节流对象(本地流)
客户端和服务器之间进行读写,必须使用Socket中提供的字节流对象(网络流)
read方法阻塞,解决:上传完文件,给服务器一个结束标记
void shutdownOutput() 禁用此套接字的输出流
对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟TCP的正常连接终止序列。
浏览器解析服务器回写的html页面,页面中如果有图片,那么浏览器就会单独的开启一个线程,读取服务器的图片。
我们要让服务器一直处于监听状态,客户端请求一次,服务器就回写一次。
函数式接口:在java中是指:有且仅有一个抽象方法的接口。当然接口中可以同时包含其他方法(默认、静态、私有) @FunctionalInterface注解 作用:可以检测接口是否是一个函数式接口 是:编译成功 否:编译失败(接口中的抽象方法个数不唯一) 函数式接口的使用:一般可以作为方法的参数和返回值类型 使用Lambda优化日志案例: 使用Lambda表达式作为参数传递传递,仅仅是把参数传递到showLog方法中只有满足条件,日志的等级是1级 才会调用接口MessageBuilder中的方法builderMessage,才不会进行字符串的拼接。 如果不满足条件,日志的等级不是1级 那么MessageBuilder接口中的方法builderMessage也不会执行,所以拼接字符串的代码也不会执行。 Lambda的特点:延迟加载 Lambda的使用前提,必须存在函数式接口 例如java.lang.Runnable接口就是一个函数式接口。 假设有一个startThread方法使用该接口作为参数,那么就可以使用Lambda进行传参。这种情况其实和Thread类的构造方法参数没有本质区别。 如果一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个Lambda表达式。 当需要通过一个方法来获取一个java.util.Comparator接口类型的对象作为排序器时,就可以调该方法获取。 常用的函数式接口: java.util.function.Supplier<T>接口仅包含一个无参的方法:T get()。 用来获取一个泛型参数指定类型的对象数据。 Supplier<T>接口被称为生产型接口,指定接口泛型是什么类型,那么接口中的get方法就会产生什么类型的数据 java.util.function.Consumer<T>接口正好与Supplier接口相反,它不是产生一个数据,而是消费一个数据,其数据类型由泛型决定。 Consumer接口中包含抽象方法void accept(T t),意为消费一个指定泛型的数据。 Consumer接口是一个消费性接口,泛型执行什么类型,就可以使用accept方法消费什么类型的数据,至于具体怎么消费(使用),则需要自定义(输出,计算......) Consumer接口的默认方法andThen 作用:需要两个Consumer接口,可以把两个Consumer接口组合到一起,再对数据进行消费。 例如: Consumer<String> con1 Consumer<String> con2 String s="hello"; con1.accept(s); con2.accept(s); 连接两个Consumer接口,再进行消费 con1.andthen(con2).accept(s);谁写前面谁先消费 java.util.function.Predicate<T>接口 作用:对某种数据类型的数据进行判断,结果返回一个boolean值。 Predicate接口中包含一个抽象方法: boolean test(T t):用来对指定数据类型数据进行判断的方法 结果: 符合条件,返回true 不符合条件,返回false 逻辑表达式:可以连接多个判断条件 && || ! Predicate接口中有一个默认方法and,表示并且关系,也可以用于连接两个判断条件 default Predicate<T> and (Predicate<? super T> other){ Objects.requireNonNull(other); return (t)->this.test(t) && other.test(t); } 方法内部的判断条件,也是用&&运算符连接起来的。 Predicate接口中有一个默认方法or,表示或者关系,也可以用于连接两个判断条件 default Predicate<T> or (Predicate<? super T> other){ Objects.requireNonNull(other); return (t)->this.test(t) || other.test(t); } Predicate接口中有一个默认方法negate,也表示取反的意思 default Predicate<T> negate(){ return (t) -> !test(t); } java.util.function.Function<T,R>接口用来根据一个类型得到另一个类型的数据,前者称为前置条件,后者称为后置条件。 Function接口中最主要的抽象方法为: R apply(T t),根据类型T的参数获取类型R的结果。使用的场景例如:将String类型转换为Integer类型。 Function接口中的默认方法andThen:用来进行组合操作。
Stream流 说到Stream便容易想到I/O Stream,而实际上,谁规定“流”就一定是“I/O流”呢?在java8中,得益于Lambda所带来得函数式编程,引入一个全新的Stream概念,关注的是做什么,而不是怎么做,用于解决已有集合类库既有的弊端。 这里的filter、map、skip都是对函数模型进行操作,集合元素并没有真正被处理。只有当终结方法count执行的时候,整个模型才会按照指定策略执行操作。而这得益于Lambda的延迟执行特性。 ”Stream流“其实是一个集合元素的函数模型,它并不是集合,也不是数据结构,其本身并不储存任何元素(或其地址值)。 Stream(流)是一个来自数据源的元素队列 元素是特定类型的对象,形成一个队列。java中的Stream并不会存储元素,而是按需计算。 数据源 流的来源。可以是集合,数组等。 和以前的Collection操作不同,Stream操作还有两个基础的特征: 1.Pipelining:中间操作都会返回流对象本身。这样多个操作可以串联成一个管道,如同流式风格(fluent style)。这样可以对操作进行优化,比如延迟执行(laziness)和短路(short-circuiting)。 2.内部迭代:以前对集合遍历都是通过Iterator或者增强for的方式,显示的在集合外部进行迭代,这叫做外部迭代。Stream提供了内部迭代的方式,流可以直接调用遍历方法。 当使用一个流的时候,通常包括三个基本步骤:获取一个数据源(source)——>数据转换——>执行操作获取想要的结果,每次转换原有Stream对象不改变,返回一个新的Stream对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道。 java.util.stream.Stream<T>是Java8 新加入的最常用的流接口。(这并不是一个函数式接口) 获取一个流非常简单,有以下几种常用的方式: -所有的Collection集合都可以通过stream默认方法获取流; default Stream<E> stream() -Stream接口的静态方法of可以获取数组对应的流。 static <T> Stream<T> of (T...values) 参数是一个可变参数,那么我们可以传递一个数组。 流模型中常用的API: 延迟方法:返回值类型不再是Stream接口自身类型的方法,因此支持链式调用。(除了终结方法外,其余方法均为延迟方法。) 终结方法:返回值类型不再是Stream接口自身类型的方法,因此不再支持类似StringBuilder那样的链式调用。终结方法有count和forEach方法等。 Stream流中的常用方法forEach void forEach(Consumer<? super T> action); 该方法接受一个Consumer接口函数,会将每一个流元素交给该函数进行处理。 Consumer接口是一个消费性的函数式接口,可以传递Lambda表达式,消费数据。 简单记:forEach方法用来遍历流中的数据,是一个终结方法,遍历之后不能继续调用Stream流中的其他方法。 Stream流中的常用方法filter:用于对Stream流中的数据进行过滤 Stream<T> filter(Predicate<? super T> predicate); filter方法的参数Predicate是一个函数式接口,所以可以传递Lambda表达式,对数据进行过滤。 Predicate中的抽象方法: boolean test(T t); Stream流属于管道流,只能被消费(使用)一次 第一个Stream流调用完毕方法,数据就会流转到下一个Stream上,而这时第一个Stream流已经使用完毕,就会关闭了,所以第一个Stream流就不能再调用方法了。 会抛出 java.lang.IllegalStateException: stream has already been operated upon or closed 异常
如果需要将流中的元素映射到另一个流中,可以使用map方法。 <R> Stream<R> map(Function<? super T, ? extends R> mapper); 该接口需要一个Function函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。 Function中的抽象方法:R apply(T t);正如旧集合Collection当中size方法一样, Stream流中的常用方法 count:用于统计Stream流中元素的个数 long count(); count方法是一个终结方法,返回值是一个long类型的整数,所以不能再继续调用Stream流中的其他方法了。 Stream流中的常用方法limit:用与截取流中的元素 limit方法可以对流进行截取,只取用前n个。 方法签名: Stream<T> limit(long maxSize); 参数是一个long型,如果集合当前长度大于参数则进行截取,否则不进行操作。 limit方法是一个延迟方法,只是对流中的元素进行截取,返回是一个新的流,所以可以继续调用Stream流中的其他方法。 Stream流中常用的方法 skip:用于跳过元素 如果希望跳过前几个元素,可以使用skip方法获取一个截取之后的新流: Stream<T> skip(long n); 如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流。 Stream 流中的常用方法 concat:用于把流组合到一起 如果有两个流,希望合并成为一个流,那么可以使用Stream接口的静态方法concat static <T> Stream<T> concat(Stream<? extends T> a,Stream<? extends T> b) 方法引用符 双冒号::为引用运算符,而它所在的表达式被称为方法引用。如果Lambda要表达的函数方案已经存在于某个方法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的代替者。
public static void main(String[] args) { //第一种语义是指:拿到参数之后经Lambda之手,继而传递给System.out.println方法去处理。 printString(s-> System.out.println(s)); /* * 分析: * Lambda表达式的目的,打印参数传递的字符串。 * 把参数s,传递给System.out对象,调用out对象中的方法println对字符串进行了输出 * 注意: * 1.System.out对象是已经存在的 * 2.println方法也是已经存在的 * 所以可以使用System.out方法直接引用(调用)println方法。 * */ //第二种等效写法的语义是指:直接让System.out中的println方法来取代Lambda。两种写法的执行效果完全一样,而第二种方法引用 //的写法复用了已有方案,更加简洁。 //注:Lambda中 传递的参数一定是方法引用中的那个方法可以接受的类型,否则会抛出异常。 printString(System.out::println); } //Printtable 是自定义的函数式接口包含一个抽象方法print() public static void printString(Printable p){ p.print("HelloWorld"); } |
MethodRerObject obj=new MethodRerObject(); printString(obj::printUpperCaseString);通过类名引用静态成员方法,类已经存在,静态成员方法也已经存在,就可以通过类名直接引用静态成员方法。
int b=method(-10,Math::abs); System.out.println(b);通过super引用父类成员方法,如果存在继承关系,当Lambda中需要出现super调用时,也可以使用方法引用进行代替。首先是函数式接口
method(super::sayHello);通过this引用成员方法,使用方法引用优化Lambda表达式,this是已经存在的,本类的成员方法buyHouse也是已经存在的,所以我们可以直接使用this引用本类的成员方法buyHouse
marray(this::byHouse);使用方法引用优化Lambda表达式,构造方法new Person(String name) 已知, 创建对象已知 new,就可以使用Person引用new创建对象。
printName("迪丽热巴",name->new Person(name));数组的构造器引用 定义一个方法,方法的参数传递创建数组的长度和ArrayBuilder接口;方法内部根据传递的长度使用ArrayBuilder中的方法创建数组并返回。 使用方法引用优化lambda表达式,已知创建的就是int[]数组,数组的长度也是已知的,就可以使用方法引用,int[]引用new,根据参数传递的长度来创建数组。
int[] in1=createArray(10,int[]::new);
这篇关于java基础(4)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-28一步到位:购买适合 SEO 的域名全攻略
- 2024-12-27OpenFeign服务间调用学习入门
- 2024-12-27OpenFeign服务间调用学习入门
- 2024-12-27OpenFeign学习入门:轻松掌握微服务通信
- 2024-12-27OpenFeign学习入门:轻松掌握微服务间的HTTP请求
- 2024-12-27JDK17新特性学习入门:简洁教程带你轻松上手
- 2024-12-27JMeter传递token学习入门教程
- 2024-12-27JMeter压测学习入门指南
- 2024-12-27JWT单点登录学习入门指南
- 2024-12-27JWT单点登录原理学习入门