1、java编程基础
1、输入
创建对象:Scanner sc = new Scanner(System.in);
接收数据:int i = sc.nextInt( );
2、产生随机数
创建对象:Random r = new Random( );
接收数据:int number = r.nextInt(10);
【范围0=<number<10】
3、关键字
(1)private关键字
① 是一个权限修饰符
② 可以修饰成员(成员变量和成员方法)
③ 外界无法访问
作用:保护成员不被别的类使用,只能在本类中使用。
针对private修饰的成员变量,如果需要被其他类使用,提供getter()和setter(参数)方法。
(2)this关键字
this关键字:this修饰的变量用于指代成员变量。
(3)super
super和this的用法相通,super用于父类对象的引用。
关键字 | 访问成员变量 | 访问构造方法 | 访问成员方法 |
---|---|---|---|
this | this.成员变量 ,访问本类成员变量 | this(...) ,访问本类构造方法 | this.成员方法(...) ,访问本类成员方法 |
super | super.成员变量 ,访问父类成员变量 | super(...) ,访问父类构造方法 | super.成员方法(...) ,访问父类成员方法 |
2、java中的常用类
1、String类
1、字符串输入
创建对象:Scanner sc = new Scanner(System.in);
接收数据:String line = sc.nextLine();
注意:String字符串不可变,他们的值在创建后不能被更改,但是值可以共享。
2、String构造方法
public String();//创建一个空白的字符串对象,不含有任何内容。
public String(char[] chs);//根据字符数组的内容来创建字符串对象。
public String(byte[] bys);//根据字节数组的内容来创建字符串对象。
推荐:String s = "abc";//直接赋值
特点:
· 通过new创建的字符串对象,每一次new都会申请一个内存空间,如果内容相同,但是地址不同。
· 通过直接赋值创建的String,如果内容相同,地址也相同。
3、字符串比较
① 使用“ == ”作比较
基本类型:比较数值是否相同。
引用类型:比较的是地址值是否相同。
② 通过equals()方法来比较字符串对象的内容是否相同
public boolean equals(Object obj);
//将此字符串与指定对象作比较,参数值直接传递一个字符串对象。
例:
字符串s1,字符串s2:s1.equals(s2);
③ 字符串遍历
· 获取字符串中每一个字符的方法:
public char charAt(int index);//返回索引处的char值,索引从0开始。
· 获取字符串长度:
public int length();//返回字符串长度。
例:
数组长度:数组名.length;
字符串长度:字符串对象.length();
· 遍历字符串通用格式:
for(int i=0; i<s.length(); i++){
s.charAt(i);//指定索引处的字符串
}
4、String类中的常用方法
int indexOf(int ch);//返回指定字符在此字符串中第一次出现处的索引
int lastIndexOf(int ch);//返回指定字符在此字符串最后一次出现处的索引
int indexOf(String str);//返回指定子字符串在此字符串中第一次出现处的索引
int lastIndexOf(String str);//返回指定子字符串在此字符串中最后一次出现处的索引
char charAt(int index);//返回字符串中index位置上的字符,,index从0开始
boolean endsWith(String suffix);//判断此字符串是否以指定字符串结尾
int length();//返回此字符串的长度
boolean equals(Object obj);//将此字符串与指定字符串作比较
boolean isEmpty();//当且仅当字符串长度为0时返回true
boolean startsWith(String prefix);//判断此字符串是否以指定字符串开始
boolean contains(CharSequence cs);//判断此字符串中是否包含指定的字符串序列
String toLowerCase();//将此字符串中的字符都转换为小写
String toUpperCase();//将此字符串中的字符都转换为大写
static String valueOf(int i);//返回int参数的字符串表示形式
char[] toCharArray();//将此字符串转换为字符数组
String replace(CharSequence oldstr,CharSequence newstr);//返回一个新的字符串,它是通过用newstr替换此字符串中出现的所有oldstr得到的
String split(String regex);//根据参数regex将此字符串分割为若干个子字符串。(regex是一个正则表达式,用来限定分割规则)
String substring(int beginIndex);//返回从beginIndex截取到末尾的字符串
String substring(int beginIndex,int endIndex);//返回从beginIndex截取到endIndex的字符串
String trim();//返回一个新字符串,它去除了原字符串首尾的空格
byte[] getBytes();//返回字符串对应的字节数组
2、StringBuilder类
public static void main(String[] args){
String s = "hello";
S + = "world";
System.out.println(s);
}
地址:
001:hello
002:world
003:helloworld
解释:如果对字符串进行拼接操作,每次拼接都会构建一个新的String对象,既耗时又浪费资源,而这种操作不可避免,但是StringBuilder可解决。
① StringBuilder是一个可变的字符串类。String内容不可变,StringBuilder内容可变。
② StringBuilder构造方法:
public StringBuilder();//创建一个空白可变的字符串对象,不含任何内容
public StringBuilder(String str);//根据字符串内容,创建可变的字符串对象
③ StringBuilder的添加和反转方法:
public StringBuilder append(任意类型参数);//添加数据,并返回对象本身
public StringBuilder reverse();//返回相反的字符串序列
④ StringBuilder转换为String:
public String toString();
例:
StringBuilder sb = new StringBuilder();
String s = sb.toString();
⑤ String转换为StringBuilder:
public StringBuilder(String s);//通过构造方法即可转化
例:
String s = " ";
StringBuilder sb = new StringBuilder(s);
3、Math类的常用方法
public static int abs(int a);//返回参数的绝对值
public static double ceil(double a);//返回大于或等于参数的最小double值,等于一个整数
public static double floor(double a);//返回小于或等于参数的最大double值,等于一个整数
public static int round(float a);//按照四舍五入返回最接近参数的int值
public static int max(int a, int b);//返回两个数中较大的值
public static int min(int a, int b);//返回两个数中较小的值
public static double pow(double a, double b);//返回a的b次幂的值
public static double random();//返回值为double的正值,值大于等于0.0小于1.0
4、System类的常用方法
① public static void exit(int status)
:终止当前运行的java虚拟机,非0表示异常终止
② public static long currentTimeMillis()
:返回当前时间(以毫秒为单位)
5、Object类
概述:Object是类层次结构的根,每个类都可以将Object类作为超类。所有的类都直接或者间接继承该类。
Object类的常用方法:
public String toString();//返回对象的字符串表示形式。建议所有子类重写该方法
public boolean equals(Object obj);//比较对象是否相等。默认比较地址,重写可比较内容
6、基本包装类
① int和String之间的转化
int转为String:
public static String valueOf(int i);//返回int参数的字符串表示形式。(String类中的方法)
String转为int:
public static int parseInt(String s);//将字符串解析为int类型。(Integer类中的方法)
7、Date类
概述:Date代表了一个特定的时间,精确到毫秒。
构造方法:
public Date();//分配一个Date对象并初始化,它代表被分配的时间,精确到毫秒
public Date(long date);//分配一个date对象,并将其初始化为从标准基准时间起指定的毫秒数
Date类的常用方法:
public long getTime();//获取的是date对象从1970年1月1日0时0分0秒到现在的毫秒值
public void setTime(long time);//设置时间,给的是毫秒值
8、SimpleDateFormat类
概述:SimpleDateFormat是一个具体的类,用于格式化和解析日期
常用的模式字母以及对应关系如下:y:年 M:月 d:日 H:时 m:分 s:秒
构造方法:
public SimpleDateFormat();//构造一个SimpleDateFormat使用默认的日期格式
public SimpleDateFormat(String pattern);//构造一个SimpleDateFormat使用指定的日期格式
SimpleDateFormat格式化和解析日期:
① 格式化(从Date到String):
public final String format(Date date);//将日期格式化成日期/时间字符串
② 解析(从String到Date)
public Date parse(String source);//从给定字符串的开始解析文本以生成日期
9、泛型
泛型定义格式:
① <类型>:指定一种类型的格式。这里的类型可以看成形参
② <类型1,类型2,...>:指定多种类型的格式,多种类型之间用逗号隔开。这里的类型可以看成是形参。
③ 将来具体调用的时候给定的类型可以看成实参,并且实参的类型只能是引用数据类型。
泛型类:
格式:修饰符 class 类名<泛型>{}
例:public class Generi<E> {}
注意:此处E可以编写为任意字符,常见如:T、E、K、V等形式参数用于表示泛型。
泛型接口:
格式:修饰符 interface 接口名 <泛型>{}
例:public interface Generi <T> {}
泛型方法:
格式:修饰符 <泛型> 返回值类型 方法名(类型 变量名){}
例:piblic <T> void show(T t){}
10、类型通配符
类型通配符:为了表示各种泛型List的父类,可以使用泛型通配符。
· 类型通配符:<?>
· List<?>:表示元素类型未知的List,它的元素可以匹配任何的类型。
· 这种带通配符的List仅表示它是任何泛型的父类,并不能把元素添加到其中。
· 如果说我们不希望List<?>是任何泛型的父类,只希望它代表某一泛型List的父类,可以使用通配符的上限。
类型通配符上限:<? super 类型>
例:
List<? extends Number>:它表示的类型是Number或其子类型。
· 除了可以使用指定类型通配符的上限,我们也可以指定类型通配符的下限
类型通配符下限:<? super 类型>
例:
List<? super Number>:它表示的类型是Number或其父类型。
11、可变参数
可变参数:可变参数又称参数个数可变,用作方法得形参出现,那么方法形参的个数就是可变的了。
格式:修饰符 返回值类型 方法名(数据类型... 变量名){}
例:public int sum(int... a){}
可变参数注意事项:
这里的变量a其实是一个数组。
如果一个方法有多个参数,包含可变参数,可变参数要放在最后。
可变参数的使用:
① Arrays工具类中有一个静态方法:
public static <T> List<T> asList(T... a);//返回由指定数组支持的固定大小的列表。
注意:返回的集合不能做增删操作,但是可以做修改操作。
② List接口中有一个静态方法:
public static <E> List<E> of(E... element);//返回包含任意数量元素的不可变列表。
注意:返回的集合不能做增删改操作。
③ Set接口中有一个静态方法:
public static <E> Set<E> of(E... element);//返回一个包含任意数量元素的不可变集合。
注意:在给集合元素的时候不能给重复元素;返回的集合不能做增删操作,没有修改操作。
3、集合
概述:提供空间可变的存储类型,存储的数据容量可以发生改变。(相当于一个可变可修改的内存)。
格式:ArrayList<E>
; //<E>是一种特殊的数据类型,泛型。在出现E的地方可以用数据类型替换。
例:
ArrayList<String>、ArrayList<Student>
1、ArrayList构造方法和添加方法
public ArrayList();//创建一个空的集合对象
public boolean add(E e);//将指定的元素追加到此集合的末尾
public void add(int index,E element);//在此集合的指定位置加入指定元素
2、ArrayList集合常用的方法
public boolean remove(Object obj);//删除指定元素,返回删除是否成功
public E remove(int index);//删除指定元素,返回被删除元素
public E set(int index,E element);//修改指定元素,返回被修改元素
public E get(int index);//返回指定元素
public int size();//返回集合中元素的个数
3、Arrays类的概述和常用方法
概述:Arrays包含用于操作数组的各种方法。
常用方法:
public static String toString(int[] a);//返回指定数组的内容的字符串表示形式。
public static void sort(int[] a);//按照数字顺序排序指定数组
4、Collection集合的概述和使用
概述:
是单列集合的顶层接口,它表示一组对象,这些对象也称为Colleation元素。
JDK不提供此接口的任何直接实现,它提供更具体的子类接口(如List)实现。
创建集合对象:
· 多态方式
· 具体的实现类ArrayList
例:Collection<String> c = new ArrayList<String>();
Collection集合的常用方法:
boolean add(E e);//添加元素
boolean remove(Object obj);//从集合中移除指定元素
void clear();//清空集合中的元素
boolean contains(Object obj);//判断集合中是否存在指定元素
boolean isEmpty();//判断集合是否为空
int size();//返回集合长度,也就是集合中元素的个数
Collection集合的遍历:
Iterator:迭代器,集合的专用遍历方式
· Iterator<E> iterator();
//返回此集合中元素的迭代器,通过集合的iterator方法得到。
例:Iterator<String> it = c.iterator();
· 迭代器是通过集合的iterator()方法得到的,所以我们说它依赖集合存在。
Iterator中的常用方法:
E next();//返回迭代中的下一个元素
boolean hashNext();//如果迭代具有更多元素,则返回true
注意:Iterator是一个接口
专用遍历方式:
while(it.hashNext()){
System.out.println(it.next());
}
5、List集合概述和特点
概述:
继承了Collection。
有序集合(也成为序列),用户可以精确控制列表中每个元素的插入位置。用户可以通过整数索引访问元素。
与set不同,列表允许存储重复元素。
List集合中的方法与ArrayList集合中的方法一样。
List创建对象:List<String> list = new ArrayList<String>();
并发修改异常:ConcurrentMondifiCationException
产生原因:迭代器遍历过程中,通过集合对象修改了集合中元素的长度,造成了迭代器获取元素中判断预期修改值和实际值不一致。
ListIterator:列表迭代器
继承了Iterator
通过List集合的listIterator方法得到,所以说它是List集合特有的迭代器。
用于允许程序员沿任意方向遍历列表的列表迭代器,它在迭代期间修改列表,并获取列表中迭代器的当前位置。
List集合的常用方法:
E next();//返回迭代中的下一个元素
boolean hashNext();//如果迭代器中具有更多元素,则返回true
E previous();//返回列表的上一个元素
boolean hashPrevious();//如果此集合列表迭代器在相反方向遍历时具有元素,返回true
void add(E e);//将指定元素加入列表
List集合子类特点:
常用子类:ArrayList、LinkedList
ArrayList:底层数据结构是数组,查询快,增删慢。
LinkedList:底层数组结构是链表,查询慢,增删快。
注意:ArrayList和LinkedList是List的子类,所以List有的功能,他俩都有。
LinkedList集合的特有功能:
public void addFirst(E e);//在此列表开头加入指定元素
public void addLast(E e);//在列表末尾加入指定元素
public E getFirst();//返回此列表中第一个元素
public E getLast();//返回此列表中最后一个元素
public E removeFirst();//删除并返回此列表中第一个元素
public E removeLast();//删除并返回此列表中最后一个元素
6、增强for循环
作用:简化数组和collection集合的遍历
实现Interator接口的类允许其对象成为增强型for语句的目标。
其内部原理是一个Interator迭代器。
格式:
for(元素数据类型 变量名:数组或collection集合){
//在此处使用变量即可
}
例:
int[] arr = {1,2,3};
for(int i : arr){
System.out.println(i);
}
7、set集合概述和特点
特点:
继承了Collection。
不包含重复元素。
没有带索引的方法,所以不能使用普通for循环遍历。
创建对象:Set<String> set = new HashSet<String>();
注意:HashSet对集合的迭代顺序不做任何保证。
8、哈希值
哈希值:是JDK根据对象的地址或者字符串或者数字计算出来的int类型的数值。
注意:哈希值是int类型的数值,不是地址值。
① Object类中有一个方法可以获取对象的哈希值:
public int hashCode();//返回对象的哈希值
②对象的哈希值特点:
同一个对象多次调用hashCode()方法返回的哈希值是一样的。
默认情况下,不同对象的hash值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同。
HashSet集合特点:
底层数据结构是哈希表。
对集合迭代顺序不做任何保证,也就是说不保证存入顺序和取出顺序一致。
没有带索引的方法,所以不能使用普通for循环遍历。
由于是Set集合,所以不能包含重复元素。
要保证元素唯一性,需要重写hashCode()和equals(),自动生成即可。
LinkedListHashSet集合特点:
哈希表和链表实现的Set接口,具有可预测的迭代次序。
由链表保证元素有序,也就是说元素的存入顺序和取出顺序是一致的。
由哈希表保证元素唯一,也就是说没有重复元素。
TreeSet集合概述和特点:
· TreeSet继承了Set
· 元素有序,这里的有序不是指存入和取出的顺序,而是按照一定规则进行排序,具体的排序方式取决于构造方法。
· TreeSet()
:根据元素的自然排序进行排序。
· TreeSet(Comparator comparator)
:根据指定的比较器进行排序。
· 没有带索引的方法,所以不能使用普通for循环遍历。
· 由于是Set集合,所以不包含重复元素。
自然排序Comparable的使用:
用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序。
自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(To)方法。
重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写。
比较器Comparator的使用(升序this放前面,降序this放后面):
用TreeSet集合存储指定对象,带参构造方法使用的是比较器排序对元素进行排序。
比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(o1, o2)方法。
重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写。
9、Map集合的概述和使用
创建Map集合对象:Map<String,String> map = new HahMap<String,String>();
Map集合的常用方法:
V put(K key,V value);//添加元素
V remove(Object obj);//根据键删除键值对元素
void clear();//移除所有键值对元素
boolean containsKey(Object key);//判断集合是否包含指定的键
boolean containsValue(Object value);//判断集合是否包含指定的值
boolean isEmpty();//判断集合是否为空
int size();//返回集合的长度,也就是集合中键值对的个数
V get(Object key);//根据键获取值
Set<K> keySet();//获取所有键的集合
Collection<V> values();//获取所有值的集合
Set<Map.Entry<k,v>> entrySet();//获取所有键值对对象的集合
Map集合的遍历:
方式一:Map存储的元素都是成对出现的,所以我们可以把Map看成是一个夫妻对集合。
思路:
把所有丈夫给集中起来。
遍历丈夫的集合,获取每一个丈夫。
根据丈夫去找对应的妻子。
操作:
获取所有键的集合,用keySet实现
遍历键的集合,获取到每一个键,用增强for实现
根据值去找值。用get(Object key)实现
方式二:Map存储的元素都是成对出现的,所以我们可以把Map看成是一个夫妻对集合。
思路:
获取所有结婚证的集合。
遍历结婚证的集合,得到每一个结婚证。
根据结婚证去获取丈夫和妻子。
操作:
获取所有键值对对象的集合,用Set<Map.Entry<K,V>> entrySet();
遍历键值对对象的集合,得到每一个键值对对象,用增强for实现,得到每一个Map.Entry。
根据键值对对象获取键和值。
用getKey获取键,用getValue获取值
TreeMap用法根HashMap用法一样,只不过对键进行了排序。
10、Collections概述和使用
概述:是针对集合操作的工具类。
Collections类的常用方法:
public static <T extends Comparable<? super T>> void sort(List<T> list);//将指定的列表按照升序排序。
public static void reverse(List<?> list);//反转指定列表中的元素
public static void shuffle(List<?> list);//使用默认的随机源随机排列指定的列表
4、I/O流
1、File类概述和构造方法
概述:它是文件和目录路径名的抽象表示。
文件和目录路径是可以通过File封装成对象的。
对于File而言,其封装的并不是一个真正存在的文件,仅仅是一个路径名而已。它可以是存在的,也可以是不存在的。将来是要通过具体的操作把这个路径的内容转换为具体存在的。
File类的构造方法:
File(String pathName);//通过给定的路径名字符串转换为抽象路径名来创建新的File实例
File(String parent,String child);//从父路径名字符串和子路径名字符串来创建新的File实例
File(File parent,String child);//从父抽象路径名和子路径名字符串创建新的File实例
File类创建功能(创建目录和文件的方法):
public boolean createNewFile();//当具有该名称的文件不存在时,创建一个由该抽象路径名命名的新文件。
public boolean mkdir();//创建由此抽象路径名命名的目录
public boolean mkdirs();//创建由此抽象路径名命名的目录(常见多级目录)
注意:
· 如果文件不存在就创建文件,并返回true
· 如果文件存在就不创建文件,并返回false
File类的功能:
public boolean isDirectory();//测试此抽象路径名表示的File是否为目录
public boolean isFile();//测试此抽象路径名表示的File是否为文件
public boolean exits();//测试此抽象路径名表示的File是否存在
public String getAbsolutePath();//返回此抽象路径名的绝对路径名字符串
public String getPath();//将此抽象路径名转换为路径名字符串
public String getName();//返回此抽象路径名表示的文件或者目录名称
public String[] list();//返回此抽象路径名表示的目录中的文件和目录的名称字符串数组
public File[] listFiles();//返回此抽象路径名表示的目录中的文件和目录的File数组对象
public boolean delete();//删除由此抽象路径名表示的文件或目录
注意:如果一个目录中由内容(目录,文件)不能直接删除,应先删除目录中的内容,再删除目录。
2、IO流概述和分类
IO流概述:
IO:输入/输出(Input/Output)。
流:是一种抽象概念,是对数据传输的总称。也就是说数据在设备间的传输称为流,流的本质是数据传输。
IO流就是用来处理设备数据传输问题的。
常见应用:文件复制、文件上传、文件下载。
IO流分类:
按照数据传输方向:
输入流:读数据
输出流:写数据
按照数据类型来分:
字节流:字节输入流;字节输出流
字符流:字符输入流;字符输出流
一般来说,我们说IO流分类是按照数据类型来分的。
这两种情况的使用情况:如果数据通过window自带的记事本软件打开,我们还可以读懂里面的内容,就使用字符流,否则使用字节流。如果不知道使用哪种类型的流,就使用字节流。
3、字节流写数据
① 字节流抽象基类:
InputStream:这个抽象类是表示字节输入流的所有类的超类。
OutputStream:这个抽象类是表示字节输出流的所有类的超类。
子类名特点:子类名称都是以其父类名作为子类名的后缀。
② FileOutputStream:文件输出流用于将数据写入File
FileOutputStream(String name):创建文件输出流以指定的名称写入文件(构造方法)
③ 使用字节输出流写数据的步骤:
创建字节输出流对象(调用了系统功能创建文件,创建字节输出流对象,让字节输出流对象指向文件)。
调用字节输出流对象的写数据方法。
释放资源(关闭此文件输出流并释放与此输出流相关联的任何系统资源)。
字节流写入的三种方式:
① 构造方法
FileOutputStream(String name);//创建文件输出流以指定的名称写入文件
FileOutputStream(File file);//创建文件输出流以写入由指定的File对象表示的文件
② 写数据的三种方式
void write(int b);//将指定的字节写入此文件输出流,一次写一个字节数据。
void write(byte[] b);//将b.length字节从指定的字节数组写入此文件输出流,一次写一个字节数组。
void write(byte[] b,int off,int len);//将len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流,一次写一个字节数组的部分数据。。
byte[] getBytes();//返回字符串对应的字节数组。(String类中的方法)
字节流写数据的两个小问题:
① 字节流写数据如何换行?
答:写完数据后,加换行符:window \r\n Linux \n mac \r
②字节流写数据如何实现追加输入?
答:public FileOutputStream(String name,boolean append);//创建文件输出流以指定的名称写入文件。如果第二个参数为true,则字节将写入文件的末尾而不是开头。
4、字节流读数据(一次读一个字节数据):
FileInputStream:从文件系统中的文件获取输入字节
FileInputStream(String name);//通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name命名。
使用字节输入流读数据的步骤:
① 创建字节输入流对象
② 调用字节输入流对象的读数据方法
③ 释放资源
读数据的三种方式:
int read();//从输入流中读取一个数据字节,如果达到末尾返回-1
int read(byte[] b);//从输入流中读取一定数量的字节,并将其存储到缓冲区数组b
int read(byte[] b,int off,int len);//从输入流中读取多达len字节的数据到字节数组中
· String(byte[] bytes,int offset,int length);//将byte中指定的字节,构造一个新的String(构造方法)
注意:在第二个方法中返回的int是实际读取字节的个数。
5、字节缓冲流:
① 字节缓冲流:
BufferOutputStream:该类实现输出缓冲流。通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用。
BufferInputStream:创建BufferInputStream将创建一个内部缓冲区数组。当从流中读取或跳过字节时,内部缓冲区键给根据需要从所包含的输入流中重新填充,一次很多字节。
② 构造方法:
字节缓冲输出流:BufferedOutputStream(OutputStream output)
字节缓冲输入流:BufferedInputStream(InputStream input)
③ 为什么构造方法需要的是字节流,而不是具体的文件或者路径呢?
答:字节缓冲流仅仅提供缓冲区,而真正的读写数据还得依靠基本的字节流对象进行操作。
6、字符流:
为什么会出现字符流?
答:由于字节流操作中文不是特别方便,所以java提供字符流。
· 字符流 = 字节流 + 编码表
· 汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数。
字符串中的编码解码问题:
① 编码(成员方法)
byte[] getBytes();//使用平台默认字符集将该String编码为一系列字节,将结果存储到新的字节数组中。
byte[] getBytes(String charsetName):使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中。
② 解码(构造方法)
String(byte[] bytes):通过使用平台默认的字符集解码指定的字节数组来构造新的String。
String(byte[] bytes,String charsetName):通过指定的字符集解码指定的字节数组来创建新的String。
字符流中的编码解码问题:
① 字符流抽象基类:
Reader:字符输入流的抽象类
Writer:字符输出流的抽象类
② 字符流中和编码解码问题相关的两个类:
· InputStreamReader是从字节流到字符流的桥梁:它读取字节,并使用指定的charset将其解码为字符。它使用的字符集可以指定,也可以平台默认。
构造方法:
· InputStreamReader(InputStream input):创建一个使用默认字符集的InputStreamReader
· InputStreamReader(InputStream input,String charsetName):创建一个使用命名字符集的InputStreamReader
· OutputStreamWriter是从字符流到字节流的桥梁:使用指定的charset将写入字符的字符编码为字节。
构造方法:
· OutputStreamWriter(OutputStream output);//创建使用默认字符编码的OutputStreamWriter
· OutputStreamWriter(OutputStream output,String charsetName);//创建一个使用命名字符集的OutputStreamWriter
字符流写数据的5种方式:
void write(int c);//写一个字符
void write(char[] arr);//写入一个字符数组
void write(char[] arr,int off,int len);//写入字符数组的一部分
void write(String str);//写一个字符串
void write(String str,int off,int len);//写入一个字符串的一部分
void flush();//刷新流
void close();//关闭流,先刷新再关闭
字符流读数据的两种方式:
int read();//一次读一个字符数据
int read(char[] arr);//一次读一个字符数组数据
转化流的名字较长,而我们常见的操作都是按照本地默认编码实现的,所以为了简写,转换流提供了对应的子类。
① FileReader:用于读取字符文件的便捷类
FileReader(String fileName);//创建一个新的FileReader,给定要读取的文件名称
② FileWriter:用于写入字符文件的便捷类
FileWriter(String fileName);//构建一个给定文件名的FileWriter对象
FileWriter(String fileName,boolean append);//构造一个FileWriter对象,给出一个带有布尔值的文件名,表示是否附加写入数据。
7、字符缓冲流:
BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符、数组和字符串的高效写入,可以指定缓冲区大小,或者可以接收默认大小。(默认值足够大)
BufferedReader:从字符输入流读取文本、缓冲字符,以提供字符、数组和行高的高效读取,可以指定缓冲区大小,或者使用默认大小。(默认值足够大)
构造方法:
BufferedReader(Reader in):创建一个使用默认大小的输入缓冲区的缓冲字符输入流
BufferedWriter(Writer out):创建一个使用默认大小的输出缓冲区的缓冲字符输出流。
字符缓冲流特有功能:
① BufferedWriter:
· void newLine();//写一行"行分隔符",行分隔符字符串由系统属性定义。(换行)
② BufferedReader:
· public String readLine();//读一行文字。结果不包含任何终止字符,如果达到结尾则为null
8、IO流小结:
① 字节流:字节流可以复制任意文件数据,有4种方式,一般采用字节缓冲流一次读写一个数组的方式。
② 字符流:字符流只能复制文本数据,有5种方式,一般采用字符缓冲流的特有功能。
9、标准输入输出流:
① System类中有两个静态的成员变量:
public static final InputStream in:标准输入流。通常该流应用于键盘输入。
public static final PrintStream out:标准输出流。通常该流应用于输出到显示屏。
② 自己实现键盘录入数据:
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
③ 自己实现键盘录入数据太麻烦了,java就提供了一个类实现键盘录入
Scanner sc = new Scanner(System.in);
④ 输出语句的本质是一个标准的输出流:
· PrintStream ps = System.out;
· System.out.printIn();
10、打印流:
① 打印流分类:
· 字节打印流:PrintStream ------> 是OutputStream的子类
· 字符打印流:PrintWriter ------> 是Writer的子类
② 打印流特点:
· 只负责输出数据,不负责读取数据。
· 有自己特定的方法。
③ 字节打印流:
PrintStream(String fileName);//使用指定的文件名创建新的打印流。
例:PrintStream ps = new PrintStream("E://java");
ps.print(100);
使用继承父类的方法写数据,查看的时候会转码;使用自己特有的方法写数据,查看的数据原样输出。
④ 字符打印流PrintWriter的构造方法:
PrintWriter(String fileName);//使用指定的文件名创建一个新的PrintWriter,不会自动刷新
PrintWriter(Writer out,boolean autoflush);//创建一个新的PrintWriter,out字符输出流,autoflush一个布尔值,如果为真PrintIn、Printf、format方法将会自动刷新输出缓冲区。
11、对象序列化和对象反序列化:
① 对象序列化:就是将对象保存在磁盘中,或者在网络中传输对象。
· 这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象类型,对象的数据和对象中存储的属性等信息。
② 要实现对象序列化和对象反序列化就要使用对象序列化流和对象反序列化流:
· 对象序列化流:ObjectOutputStream
· 对象反序列化流:ObjectInputStream
对象序列化流:ObjectOutputStream
构造方法:
ObjectOutputStream(OutputStream out);//创建一个写入指定的OutputStream的ObjectOutputStream
序列化对象的方法:
void writerObject(Object obj);//将指定的对象写入ObjectOutputStream
注意:
· 一个对象要想被序列化,该对象所属的类必须实现Serializable接口。
· Serializable是一个标记接口,实现该接口不需要重写任何方法。
对象反序列化流:ObjectInputStream
构造方法:
ObjectInputStream(InputStream in);//创建从指定的InputStream读取的ObjectInputStream
反序列化对象的方法:
Objec readObject();//从ObjectInputStream读取一个对象。
关于序列化和反序列化的一些问题:
① 用对象序列化序列化一个对象后,假如我们改变了对象所属的类文件,读取数据会不会出问题呢?
答:会出问题,会抛出一个InvalidClassException异常。
② 如果出现了问题,如何解决?
答:给对象所属的类加一个serialVersionUID,必须使用private static final long修饰
例:private static final long serialVersionUID = 42L;
③ 如果一个对象中的某个成员变量的值不想被序列化,又该如何实现?
答:给该成员变量加transient关键字
修饰,该关键字标记的成员变量不参与序列化过程。
例:private transient int age;
12、Properties集合:
概述:Properties集合是一个Map体系的集合类;Properties可以保存到流中或从流中加载。
Properties集合的特有方法:
Object setProperty(String key,String value);//设置集合的键值,都是String类型,底层调用Hashtable的put方法。
String getProperty(String key);//使用此属性列表中指定的键搜索值。
Set<String> stringPropertyNames();//从该属性列表中返回一个不可修改的键集,其中键及对应的值是字符串。
Properties和IO流结合的方法:
① 将文件中的数据加载到集合:
void load(InputStream inStream);//从输入字节流读取键和元素对
void load(Reader reader);//从输入字符流读取键和元素对
② 将集合中的数据加载到文件:
void store(OutputStream out,String comment);//将键和元素写入此Properties表中,以适合使用load方法的格式写入输出字节流。
void store(Writer writer,String comments);//将此键和元素对写入此Properties表中,以适合使用load方法的格式写入输出字节流。
5、线程
1、概述
① 进程:是指正在运行的程序。
是系统进行资源分配和调度的独立单位。
每一个进程都有它自己的内存空间和系统资源。
② 线程:是进程中的单个顺序控制流,是一条执行路径。
单线程:一个进程如果只有一条执行路径,则称为单线程程序。
多线程:一个进程如果有多条执行路径,则称为多线程进程。
多线程的实现方式一:
① 继承Thread类
定义一个类MyThread继承Thread类。
在MyThread类中重写run( )方法。
创建MyThread类对象。
启动线程。调用start( )方法。
② 两个小问题:
为什么要重写run( )方法?
答:因为run( )是用来封装被线程执行的代码。
run( )方法和start( )方法的区别?
答:run( ):封装线程执行的代码,直接调用,相当于普通方法的调用。
start( ):启动线程。由JVM调用此线程的run( )方法。
2、设置和获取线程名称:
① Thread类中设置和获取线程名称的方法:
void setName(String name);//将此线程的名称设置为name
例:my1.setName("B");
String getName();//返回此线程的名称。
通过构造方法也可以设置线程名称。
例:MyThread my1 = new MyThread("A");
② 如何获取main()方法所在的线程名称?
public static Thread currentThread();//返回对当前正在执行的线程对象的引用
例:System.out.printIn(Thread.currentThread().getName());
3、线程调度:
① 线程有两种调度模型:
· 分是调度模型:所有线程轮流使用cpu的使用权,平均分配每个线程占用cpu的时间片。
· 抢占模式调度:优先让优先级高的线程使用cpu;如果线程优先级相同,那么会随机选择一个;优先级高的线程获取cpu时间片相对多一些。
· java使用的是抢占式调度模型。
② Thread类中设置和获取线程优先级的方法:
public final int getPriority();//返回此线程的优先级。
public final int setPriority(int newPriority);//更改此线程的优先级。
注意:
· 线程默认优先级是5,线程优先级范围是0-10。
· 线程优先级高,仅仅表示线程获取cpu时间片的几率高,但是要在次数比较多,或者多次运行的时候才能看到你想要的结果。
4、线程控制:
static void sleep(long mills);//是当前正在执行的线程暂停执行指定的毫秒数。
void join();//等待这个线程死亡。(只有当这个线程结束时,才能执行其他线程)
void setDaemon(boolean on);//将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机退出。
多线程的实现方式二:
① 实现Runnable接口:
定义一个类MyRunnable实现Runnable接口。
在MyRunnable中重写run()方法。
创建MyRunnable类的对象。
创建Thread类的对象,把MyRunnable对象作为构造方法的参数。
用Thread类的对象启动线程。
② 多线程的实现方案有2种:
继承Thread类。
实现Runnable接口。
③ 相比继承Thread类,实现Runnable接口的好处
避免了java单继承的局限性。
适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码,数据有效分离,较好的体现了面向对象的设计思想。
多线程数据安全问题的解决:
①为什么出现问题?(这也是我们判断多线程程序是否会有数据安全的标准)
· 是否是多线程环境。
· 是否有数据共享。
· 是否有多条语句操作共享数据。
同时满足这三个条件,就会出现数据安全问题。
② 如何解决多线程安全问题?
· 基本思想:让程序没有线程安全的环境。(就是不让同时满足上面的三个条件)
③ 怎么实现呢?
· 把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可。
· java提供了同步代码块的方式来解决。
④ 同步代码块:
格式:synchronized(任意对象){
//多条语句操作共享数据的代码
}
synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁。
例:Private Object obj = new Object();
synchronized(obj){}
同步方法:就是把synchronized关键字加到方法上。
格式:修饰符 synchronized 返回值类型 方法名(参数){}
同步方法的锁对象是“this”
同步静态方法:就是把synchronized关键字加到静态方法上。
格式:修饰符 static synchronized 返回值类型 方法名(参数){}
同步静态方法的锁对象是“ 类名.class ”
5、Lock锁:
Lock锁比使用synchronized方法和语句可以获得更广泛的锁定操作。
① Lock中提供了获得锁和释放锁的方法:
void lock( )
:获得锁
void unlock( )
:释放锁
② Lock是接口,不能直接实例化,这里采用它的实现类ReenTrantLock来实例化。
ReenTrantLock的构造方法:
ReenTrantLock( )
:创建一个ReenTrantLock的实例。
Object类的等待和唤醒方法:
void wait();//导致当前线程等待,直到另一个线程调用该对象的notify()方法或者notifyAll()方法。
void notify();//唤醒正在等待对象监视器的单个线程。
void notifyAll():唤醒正在等待对象监视器的所有线程。
6、网络编程
1、网络编程
网络编程三要素:
① IP地址:每台计算机的唯一标识号。
② 端口:计算机中应用程序的标识。
③ 协议:位于同一网络中的计算机要想实现连接,必须要遵循一定的规则。常见的协议有UDP协议和TCP协议。
InetAddress的使用:
作用:为了方便我们对IP地址的获取和操作,Java提供了一个类InetAddress供我们使用。
InetAddress类表示Internet协议(IP)地址。
static InetAddress getByName(String host);//确定主机名称的IP地址。主机名可以是机器名称,也可以是IP地址。
String getHostName();//获取此IP地址主机名
String getHostAddress();//返回文本显示中的IP地址字符串
协议:
UDP协议:用户数据报协议。无连接通信协议,即在数据传输时,数据的发送端和接收端不设置逻辑连接。简单来说,当一台计算机向另一台计算机发送数据时,发送端不会确认接收端是否存在,就会发送数据,同样接收端在收到数据时,也不会像发送端反馈是否收到数据。
注意:由于UDP协议消耗资源小,通信效率高,通常会用于音频、视频和普通数据的传输。
TCP协议:传输控制协议。TCP是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据。要经过三次握手,才能进行数据传输。
UDP通信原理:UDP协议是一种不可靠的网络协议,它在通信的两端各建立一个Socket对象,但是这两个Socket只是发送,接收数据的对象;因此对于基于UDP协议的通信双方而言,没有所谓的客户端和服务器的概念;Java提供了DatagramSocket类作为基于UDP协议的Socket。
UDP发送数据的步骤:
① 创建发送端的Socket对象
DatagramSocket();
//构造方法
② 创建数据,并把数据打包
DatagramPacket(byte[] buf,int length,InetAddress address,int port);
③ 调用DatagramSocket对象的方法发送数据
void send(DatagramPacket p);
//发送数据
④关闭发送端
void close();
UDP接收数据的步骤:
① 创建接收端的Socket对象
DatagramSocket(int port);
//构造一个主机端口
② 创建一个数据包用于接收数据
DatagramPacket(byte[] buf,int length);
③ 调用DatagramSocket对象的方法接收数据
void receive(DatagramPacket p);
④ 解析数据包,并把数据输出在控制台
byte[] getData();
//接收数据
int getLength();
//返回要发送的数据长度或接收的数据长度
⑤ 关闭接收端
void close();
TCP通信原理:TCP通信协议是一种可靠的通信协议,它在通信两端各建立一个Socket对象,从而在通信的两端形成网络虚拟链路,一旦建立了虚拟的网络链路,两端的程序就可以通过虚拟的链路进行通信;Java对基于TCP协议的网络提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信;Java为客户端提供了Socket类,为服务器提供了ServerSocket类。
TCP发送数据的步骤:
① 创建客户端的Socket对象
Socket(String host,int port);
② 获取输出流,写数据
OutputStream getOutputStream();
③ 释放资源
void close();
TCP接收数据:
① 创建服务器端的Socket对象
ServerSocket(int port);
② 监听客户端连接,返回一个Socket对象
Socket accept();
③ 获取输入流,读数据并输出
InputStream getInputStream();
④ 释放资源
void close();
问题:程序一直等待。
原因:读数据的方式是阻塞的。
解决方法:自定义结束标记。推荐使用shutdownOutput()方法。
void shutdownOutput();
发出一个输入结束标记。(Socket类中的方法)
2、Lambda表达式:
① 组成Lambda表达式的三要素:形式参数,箭头,代码块
② 格式:(形式参数) -> {代码块}
形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可。
-> :代表指向操作。
代码块:是我们具体需要做的事情。
例:new Thread(() -> {System.out.printIn("多线程启动了")});
注意:
① Lambda表达式的前提:有一个接口,接口中有且仅有一个抽象方法。
② 使用Lambda表达式必须要有上下文环境,才能推导出Lambda对应的接口:
· 根据局部变量的赋值得知Lambda对应的接口。
· 例:Runnable r = () -> System.out.printIn("Lambda表达式");
· 根据调用方法的参数得知Lambda对应的接口。
· 例:new Thread(() -> System.out.printIn("Lambda表达式").start());
自己的理解:就是使用Lambda表达式重写接口中的抽象方法。
3、方法引用:
方法引用符(::
),方法引用符所在的表达式称为方法引用。
推导与省略:
· 如果使用Lambda表达式,那么根据“可推导就可省略”的原则,无需指定参数类型,也无需指定重载形式,他们都将自动被推导。
· 如果使用方法引用,也就是同样可以根据上下文进行推导;方法引用就是Lambda的兄弟。
① 引用类方法:其实就是引用类的静态方法。
· 格式:类名::静态方法
· 例:Integer::parseInt
② 引用对象的实例方法:其实就是引用类中的成员方法。
· 格式:对象::成员方法
· 例:"helloworld"::toUpperCase
③ 引用类的实例方法:其实就是引用类的成员方法。
· 格式:类名::成员方法
· 例:String::subString
④ 引用构造器:其实就是引用构造方法。
· 格式:类名::new
· 例:Student::new
4、函数接口:
概述:Java的函数式编程体现就是Lambda表达式,所以函数式接口就是可以适用于Lambda表达式使用的接口,只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利进行推导。
如何检测一个接口是不是函数式接口呢?
答:@FunctionalInterface注解放在函数的上方,如果接口是函数式接口,编译通过,如 果不是,编译失败。
注意:注解可写可不写,如果不写要满足函数式接口的条件,建议写上注解。
如果方法的返回值是一个函数式接口,我们可以使用Lambda表达式作为结果返回。
常用的函数式接口:Supplier接口、Consumer接口、Predicate接口、Function接口
Supplier接口:
Supplier<T>
:包含一个无参方法
· T get();
//获取结果
· 该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据。
· Supplier<T>
接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会产生什么类型的数据供我们使用。
Consumer接口:
Consumer<T>
:包含两个方法
· void accept(T t);
//对给定的参数执行此操作。
· default Consumer<T> andThen(Consumer after);
//返回一个组合的Consumer,依次执行此操作,然后执行after操作。
· Consumer<T>
接口也被称为消费型接口,它消费的数据的数据类型由泛型指定。
Predicate接口:
Predicate<T>
常用的四个方法:
boolean test(T t);//对给定的参数进行判断(判断由Lambda表达式实现),返回一个布尔值。
default Predicate<T> negate();//返回一个逻辑的否定,对应逻辑非
default Predicate<T> and(Predicate other);//返回一个组合判断,对应短路与
default Predicate<T> or(Predicate other);//返回一个组合判断,对应短路或
Predicate<T>接口通常用于判断参数是否满足指定条件。
Function接口:
Function<T,R>
常用的两个方法:
R apply(T t);//将此函数应用于给定的参数
default<V> Function andThen(Function after);//返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果。
Function<T,R>接口常应用于对参数进行处理,转换(由Lambda实现),然后返回一个值。
5、Stream流:
Stream流的使用:
① 生成流通过数据源(集合,数组等)生成流。
例:list.stream();
② 中间操作(filter()
):一个流后面可以跟随0个或多个中间操作,其目的主要是打开流,做数据过滤/映射,然后返回一个新的流,交给下一个流使用。
③ 终结操作(forEach()
):用“完”了,无法再被操作。所以这必是流的最后一个操作。
Stream流的常见生成方式:
① Collection体系的集合可以使用默认的方法stream()生成流。
· default stream<E> stream()
② Map体系的集合间接生成流。
例:Map<String,Integer> map= new HashMap<String,Integer>();
Stream<String> keyStream = map.keySet.stream();
Stream<Integer> valueStream = map.values().stream();
Stream<Map,Entry<String,Integer>> entryStream = map.entrySet().stream();
· 就是用Map中键组成的集合和值组成的集合,调用默认方法stream()生成流。
③ 数组可以通过Stream接口中的静态方法of(T values)生成流。参数是数组.
例:int[] arr = {1,2,3};
Stream.of(arr);
Stream流中常见中间操作的方法:
Stream<T> filter(Predicate predicate);//用于对流中的数据进行过滤。
· boolean test(T t);//对给定的参数进行判断,返回一个布尔值。(Predicate接口中的方法)
Stream<T> limit(long maxSize);//返回此流中的元素组成的流,截取前maxSize个数据。
Stream<T> skip(long n);//跳过指定参数个数的数据,返回由该流的剩余元素组成的流。
static <T> Stream<T> concat(Stream a,Stream b);//合并a和b两个流为一个流。
Stream<T> distinct();//返回由该流中不同元素组成的流。
Stream<T> sorted();//返回此流的元素组成的流,根据自然顺序排序。
Stream<T> sorted(Comparator comparator);//返回此流的元素组成的流,根据comparator进行排序。
<R> Stream<R> map(Function mapper);//返回由给定函数应用于此流的元素的结果组成的流。
· R apply(T t):Function中的方法
IntStream mapToInt(ToIntFunction mapper);//返回一个IntStream,其中包含将给定函数应用于此流的元素的结果。
· IntStream:表示原始int流
· ToIntFunction接口中的方法:int applyAsInt(T value)
· int sum();//返回此流中元素总和。(IntStream流中的成员方法)
stream流的常见终结操作方法:
void forEach(Consume action);//对此流的每个元素执行操作。
· void accept(T t);//对给定的参数执行此操作。(Consumer接口中的方法)
long count();//返回此流中的元素数。
stream流的收集操作:
① 对数据使用Stream流的方式操作完毕之后,把流中的数据收集到集合。
· R collect(Collector collector);//这个收集方法的参数是一个Collector接口
② 工具类Collectors提供了具体的收集方式:
public static <T> Collector toList();//把元素收集到List集合
public static <T> Collector toSet();//把元素收集到Set集合
public static Collector toMap(Function keyMapper,Function valueMapper);//把元素收集到Map集合。
例:Stream<String> listStream = list.stream();
List<String> names = listStream.collect(Collectors.toList());
6、类加载器:
① 类加载:当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载、类的连接、类的初始化这三个步骤对类进行初始化。如果不出现意外情况,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或者类的初始化。
② 类的加载:
就是指将class文件读入内存,并为之创建一个java.lang.class对象。
任何类被使用时,系统都会为之建立一个java.lang.class对象。
③ 类的连接:
验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致。
准备阶段:负责为类的类变量分配内存,并设置默认初始化值。
解析阶段:将类的二进制数据中的符号引用替换为直接引用。
④ 类的初始化:在该阶段,主要就是对类变量进行初始化。
⑤ 类初始化步骤:
假如类还未被加加载和连接,则程序先加载并连接该类。
假如该类的直接父类还未被初始化,则先初始化其直接父类。
假如类中有初始化语句,则系统依次执行这些初始化语句。
注意:在执行第二个步骤的时候,系统对直接父类初始化的步骤也遵循初始化步骤1-3。
⑥ 类的初始化时机:
创建类的实例。
调用类的类方法。
访问类或者接口的类变量,或者为该类变量赋值。
使用反射方式来强制创建某个类或接口对应的java.lang.class对象。
初始化某个类的子类。
直接使用java.exe命令来运行某个主类。
类加载器:
① 类加载器的作用:
负责将” . class “文件加载到内存中,并为之生成对应的java.lang.class对象。
虽然我们不用过分关心类加载机制,但是要了解这个机制我们能更好的理解程序的运行。
②JVM的类加载机制:
全盘负责:就是当一个类加载器负责加载某个Class时,该Class所依赖的和所引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入。
父类委托:就是当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时,才尝试从自己的类路径中加载该类。
缓存机制:保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区搜索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存储到缓存区。
③ClassLoader:是负责加载类的对象:
Java运行时具有以下内置类加载器:
Bootstrap class loader:它是虚拟机内置的类加载器,通常表示为null,并且没有父null
Platform class loader:平台类加载器可以看到所有平台类,平台类包括由平台类加载器或其他祖先定义的Java SE平台API,其实现类和JDK特定的运行时类。
System class loader:它也被称为应用程序类加载器,与平台类加载器不同。系统类加载器通常用于定义应用程序类路径,模块路径和JDK特定工具上的类。
类加载器继承关系:System的父类加载器为Platform,而Platform得父加载器为Bootstrap。
④ ClassLoader中的两个方法:
static ClassLoader getSystemClassLoader();//返回用于委派的系统类加载器。
ClassLoader getParent();//返回父类加载器进行委派。