JAVA基础(二)
一、数据结构
1、概述
数据结构(data structure):是计算机专业一门独立学科,主要研究数据的逻辑结构、存贮结构以及数据之间的关系
什么是数据:
- 在计算机系统中,各种字母和数字符号的组合、语音、图形、图像等统称为数据
- 又指所有能输入计算机并且被计算机程序处理的符号的总称,是用于输入计算机进行处理,具有一定意义的数字、字母、符号和模拟量的统称
逻辑图如下:
2、数据结构
1.逻辑结构
集合
一个存放数据的容器,数据元素间没有任何关系
线性结构
数据元素间有线性关系,分为线性表、队列、栈和串
线型关系:除第一个元素外,其他元素有且只有一个前驱,除最后一个元素外,其他元素有且只有一个后继
树结构
数据元素间有层状关系,例如二叉树
图结构
数据元素间有网状关系
2.存储结构
顺序存储结构
数据存放在磁盘连续的空间中
链式存储结构
数据存放在磁盘的任何位置,单向链式存贮(单链表)、双向链式存贮(双链表)、循环链式存贮(循环链表)
3、线性结构
1、线性表
基于数组实现线性结构 ,用数组存放数据,并保持元素之间是一个线性关系
编写线性结构的数据结构:
public void insert(int i,Object data); // 添加到线性表指定的位置;
public void append(Object data); // 追加到线性表的末尾;
public void remove(int i); // 移除元素
public void update(int i,Object data); // i为下标 ;
public Object get(int i); // 获取元素
public Object[] list();
public int size(); // 线性表元素数量;
public boolean isEmpty(); // 线性表是否为空;
对数据常见操作:检索,插入,删除,更新,排序
2、栈(stack)
常用方法:
public void push(Object data); // 添加元素
public Object pop(); // 取出元素并删除
public Object seek(); // 取出元素不删除
public int size(); // 元素数量
public boolean isEmpty(); // 判断栈是否为空
3、队列(queue)
队列特点:先进先出(FIFO)
常用方法:
boolean offer(E e); // 添加数据
Object poll(); // 删除出队
Object peek(); // 返回队列第一次元素(不删)
int size(); // 元素数量
boolean isEmpty(); // 判断队列是否为空
4、串
串:即字符串(String),是由零个或多个字符组成的有限序列。一般表示为s=“c1,c2,c3...cn”
子串:串中任意个连续的字符组成的子序列
主串:包含子串的串
空串:n=0的串
空格串:值为空格的串
4、树结构
树是一种非线性的数据结构,由n(n>0)个有限结点组成的具有层次关系的集合。
树有一个特殊的结点,叫根结点,根结点没有前驱结点。
树形结构中,子树之间不能有交集。
树的性质如下:
- 每个子树有且只有一个前驱结点
- 每个子树的根结点可以有0或多个后继结点
- 数是递归的
- 一个n个结点的数有n-1条边
5、图结构
图结构是比线性表和树结构更为复杂的数据结构。
树结构中,子树之间不能有交集,但在图结构中,是可以有交集的。
图结构中有顶点和边
顶点:图中的数据元素
边:图中连接顶点的线
所有的顶点组成一个顶点集合,所有的边组成一个边集合,组合在一起就是一个图结构
无向图:在一个图中,如果所有的边都没有方向,则称之为无向图
有向图:在一个图中,边有方向,则称之为有向图
混合图:一个图中,边同时有有向和无向的图
二、异常处理
1、理解异常
程序在运行过程中出现的问题称为异常(Exception),出现异常时的处理叫异常处理
JAVA的异常分为:
- Error:java应用程序本身无法恢复的严重错误,程序不需要捕获、处理这些严重错误
- Exception:java程序抛出和处理的非严重异常,分为运行时异常(RuntimeException)和检查异常(CheckException),也称非运行时异常
所有的异常都继承至java.lang.Throwable
类
Throwable
---- Error
---- Exception
---- RuntimeException
---- CheckException
2、Exception
需要程序员处理的,如果此类异常被捕获,程序在运行时是不会崩溃的,如果没有捕获(不做异常处理),可能导致整个程序运行崩溃
Exception异常分为2类:
- 运行时异常:正常编译,但程序运行时出的问题,又称为非检查异常
- 检查异常:此异常是在编写代码时就会以红色波浪线提示,需要做异常处理,又称非运行时异常
常见的运行时异常:
java.lang.ArrayIndexOutOfBoundsException:数组的下标越界异常
java.lang.ArithmeticException:数学运算异常
java.lang.NullPointerException:空指针异常
java.lang.ClassCastException:类型转换异常
java.lang.NumberFormatException:数字格式化异常
java.util.InputMismatchException:输出信息类型不匹配异常
常见的检查异常:
java.io.FileNotFoundException:文件找不到异常
java.io.IOException:输入输出异常
java.lang.NoSuchMethodException:方法未找到异常
3、异常处理
异常处理的三种结构:
try…catch
try{
//可能出现异常语句块
}catch(SomeException e){// 特指某些特定的异常
//当捕获到SomeException类型的异常时执行的语句块,通常是打印该异常信息
}
catch语句可以多个,但如果是Exception类型来捕获,必须为最后一个catch
如果try语句块中的代码没有异常,则try语句块执行完毕后,catch中的语句块不执行;如果try语句块有异常,则try语句块中发生异常之后的代码不执行,而由相应的catch语句进行捕获,catch中的代码会执行
try…catch…finally
try {
//可能抛出异常的语句块
}catch(SomeException e){
//当捕获到异常时运行的语句块
}finally {
//不管是否发生异常都会执行的语句块
}
finally是异常处理最后一个语句块,可加可不加,不管异常有没有发生,此语句块都会执行,finally语句块一般做的操作为资源释放,关闭流,关闭数据库连接等
try…finally
try {
//可能抛出异常的语句块
}finally {
//不管是否发生异常都会执行的语句块
}
异常处理中,唯一不执行finally语句块的一种情况是在catch语句中出现System.exit(1)的代码,则直接退出jvm,finally语句块不执行
注意事项:
- 应按照从小到大的顺序捕获异常,即应先写具体的异常类型,最后再写他们的父类Exception类型的异常
- 当try语句中有多个异常时,只会捕获第一个异常,异常后面的代码不会执行
- 捕获异常时尽量不要用return语句
- 当try/catch语句块中出现return时,先执行return语句之前的代码,再执行finally语句块,最后执行return
4、throw与throws
throw:手动抛出异常
语法:throw 异常对象
throw new Exception("异常信息...........");
throw new MyException("自定义异常.........")
throws:声明异常
在定义方法时,同时给方法声明一个异常
public void insert(int i, Object data) throws Exception{
if(i < 0){
throw new Exception("下标不合法");
}
}
通过throws声明异常后,若调用该方法,则需要处理该异常
public void insert(int i, Object data) throws Exception{
if(i < 0){
throw new Exception("下标不合法");
}
}
public void add(int i, Object data){
// 需要处理异常
try{
insert(i, data);
} catch(Exception e){
}
}
所有声明式异常都是检查异常,在编码时,就需要处理,否则语法出错
throw和throws可以同时出现,也可以单独出现,当throw单独出现时,可将throw语句放入try语句中直接进行异常处理;当throws单独出现时,可直接加在方法后面
5、自定义异常
自己写一个异常类型,处理特定异常问题
自定义异常需要继承Throwsable类或者Exception类(通常继承此类)
public class UserException extends Exception{
public UserException(String messgae) {
super(messgae);
}
}
做完自己的异常类后,在需要异常的方法中就可以通过throw语句来抛出该异常,抛出的异常可以直接throws掉让调用方处理或者直接try...catch
掉
public static void main(String[] args) throws Exception{
int i = 5;
if (i > 0){
throw new MyException("错误");
}
}
// 或者
public static void main(String[] args){
int i = 5;
if (i > 0){
try {
throw new MyException("错误");
} catch (MyException e) {
e.printStackTrace();
}
}
}
throw 与 throws区别
throw为手动抛出异常,指运行到该语句就抛出异常,不往下执行,或者可以通过try...catch
处理该异常,例如打印日志和异常堆栈信息
throws为声明式异常,在定义时,在方法的后面通过throws 指定异常类型,此时,调用该方法必须去处理该异常
三、集合与泛型
集合结构图
1、集合
集合对象是内存中的一个容器对象,用来方便的存贮批量的数据,集合中的数据在没有持久化,程序运行结束后,数据将消失。在java语言中,设计的集合主要为3大类
-
Set集合:实现Set接口的集合类称为Set集合;
-
List集合:实现List接口的集合类称为List集合;
-
Map集合:实现Map接口的集合类称为Map集合
java对集合设计的相关接口与类放在:java.util
包中,在我们使用集合时,需要导入对应的集合类或者接口
集合中只能放引用类型数据,不能放基本数据类型,如果要放,请使用基本数据类型的包装类型
Collection是Set、List、Queue三个集合共同的接口
2、Set集合
Set集合特性:不包含重复元素,通过调用元素的equals方法实现判断两个元素是不是同一个元素
public interface Set<E> extends Collection<E>
Set接口的实现类:HashSet、TreeSet
1.HashSet
基于hash算法决定元素在集合中的存放位置,HashSet存取效率非常高
熟练Set集合的基本操作API:添加、删除、获取元素、集合大小,清空集合等
获取集合元素:
1、转成数组
Set set = new HashSet();
set.add(1);
set.add(2);
set.add(3);
Object[] arr = set.toArray();
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
2、迭代器(Iterable)
迭代器:
- 一个递归获取集合元素的方式 ,需要集合实现Iterable接口,此接口定义了迭代访问集合元素的方法
- 迭代器是一个指针对象,默认指向集合第一个元素前,通过调用
hashNext()
方法可以移动迭代器
// 迭代器:Iterable
Iterator<T> iterator(); // 实现Iterable接口的方法,就能得到的一个迭代器对象
// Iterator的主要方法
boolean hasNext(); // 如果仍有元素可以迭代,则返回true
E next(); // 返回迭代的下一个元素
void remove(); // 从迭代器指向的collection中移除迭代器返回的最后一个元素
// 通过迭代器获取Set集合元素
Set set = new HashSet();
set.add(1);
// ....
Iterator it = set.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
对Set集合,如果放到集合里的是一个对象,且当对象的某个属性一致时,判断为同一个属性,如何操作?
Set集合默认是调用对象的equals方法比较,此时,已不能满足我们的业务需要
解决方法:根据新的业务重写equals方法, 此时还要重写hashcode方法
下面的例子是当tel相同时视为同一个对象:
User类:
@Data
public class User {
private int id;
private String name;
private String tel;
@Override
public int hashCode(){
// 这里重写了hashCode方法,将tel作为hashCode的值
return Integer.parseInt(tel);
}
@Override
public boolean equals(Object o){
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
User user = (User) o;
// 根据Tel是否相同判断是否为同一个人
if (tel.equals(user.getTel())) {
return true;
}
return Objects.equals(id, user.id);
}
}
测试:
public static void main(String[] args){
Set set = new HashSet();
User u1 = new User();
u1.setId(1);
u1.setName("张三");
u1.setTel("183");
User u2 = new User();
u2.setId(2);
u2.setName("李四");
u2.setTel("183");
set.add(u1);
set.add(u2);
// 可以看到,只输出了1,证明只有1个对象存进去了
System.out.println(set.size());
Iterator it = set.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
}
2.TreeSet
Set接口的第2个实现类,TreeSet相对于HashSet,多了一个排序的功能
排序方法:使用元素的自然顺序对元素进行排序
元素的自然顺序:此元素实现Comparable接口,当将元素放到TreeSet集合中,TreeSet集合就根据Comparable接口定义的比较规则来比较元素
// 返回值为整型: 为0 表示两个元素相等,大于0 表示大于,小于0 表示小于
public interface Comparable{
int compareTo(T o)
}
如果放到TreeSet集合中的元素没有实现自然排序,则意味会出异常。在JDK中,8种基本数据类型的包装类型和字符串都实现了此接口
如果在集合中放其它类型的元素,必须手动实现Comparable接口,并实现接口中定义的compareTo方法
@Data
public class User implements Comparable<User>{
private int id;
private String name;
private String tel;
@Override
public int compareTo(User o) {
// 根据id排序
int compare = Integer.compare(this.id, o.getId());
if (compare == 0) {
// 若id相同,则根据名字排序
return this.name.compareTo(o.getName());
}
return compare;
}
}
3.Set集合的一些方法
boolean add(Object obj);
:添加元素,不能重复,如果重复,则添加无效void clear();
:清空集合boolean contains(Object obj);
:判断集合中是否包含此元素boolean isEmpty();
:判断是否为空Iterator iterator();
:返回一个迭代器对象boolean remove(Object obj);
:删除元素int size();返回元素个数
:返回元素个数Object[] toArray();
:返回一个数组
3、List集合
特性:实现List接口的集合称为List集合,相对Set集合,List集合中的元素是有序的,每一个元素都有一个下标,从0开始
List集合可以放重复数据
List集合的主要实现:ArrayList,Vector,LinkedList (三个实现类的API基本一样)
public interface List<E> extends Collection<E>
ArrayList
又称为动态数组,ArrayList内部是使用数组来存放数据的,当容器添加满,会自动扩容,每次扩容至原来的1.5倍
ArrayList是线程非安全的
ArrayList对于添加、获取数据效率是比较高的
Vector
线程安全
LinkedList
LinkedList使用链表来存放数据
LinkedList对于删除、修改数据效率比较高
List集合的一些方法:
void add(int index, Object o);
:在集合的指定位置插入元素Object get(int index);
:获取指定位置的元素int indexOf(Object o);
:返回该元素第一次出现的下标int lastIndexOf(Object o);
:返回该元素最后一次出现的下标Object remove (int index);
:删除指定位置的元素Object set(int index, Object o);
:用该元素替换该位置的元素
4、Map集合
Set集合与List集合都属于单例值的集合,Map集合是双列值的集合,即集合中每一个元素是一个key - value对象,根据key可以取出对应键value的值
key - value 称为Entry对象
不能包含重复的键,每个键最多只能映射到一个值
Map集合的主要实现:HashMap、TreeMap、Hashtable、Properties
HashMap
集合中的key可以为null,值也可以为null,但只能有一个key为null,HashMap是线程非安全的
TreeMap
相对于HashMap,多了一个排序的功能,与TreeSet类似
HashTable
键和值都不能为空的,是线程安全的
Properties
Properties是一种属性文件,后缀为.properties,通常用于存储配置
该文件以key - value保存内容,格式为key=value
如何通过Map.Entry遍历Map集合:
Map<String, String> map = HashMap<>();
for(Map.Entry<String, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + " - " + entry.getValue());
}
Map集合的一些方法:
Object put(Object key, Object value);
;将指定键值添加到集合中,若与前面的相同,则替换Object get(Object key);
:返回指定键所对应的值Object remove(Object key);
:通过指定的键删除该键值对Set keySet();
:返回此Map集合中包含的键的Set集合Collection values();
:返回此Map集合中包含的值的Collection集合int size();
:返回集合的键值对的个数
5、内部比较器与外部比较器
比较器:用于集合的排序,当集合需要排序时,就需要用到比较器
内部比较器:在一个类中实现了Comparable接口,也就是在类的内部实现比较规则,称为内部比较器
外部比较器: 单独写一个类,实现比较,需要实现一个新的接口java.util.Comparator
,外部比较器一般在集合的工具类java.util.Collections
中使用
Collections工具类:主要服务于Set集合与List集合,Collections工具类中的方法一般为静态方法
使用比较器对List集合的元素排序
外部比较器实现:
// 创建一个比较器
public class MyComparator implements Comparator<User> {
@Override
public int compare(User o1, User o2) {
// 根据id排序,调用Integer的compareTo()方法
return o1.getId().compareTo(o2.getId());
}
}
public static void main(String[] args){
List<User> list = new ArrayList<>();
User u1 = new User();
u1.setId(2);
u1.setName("张三");
u1.setTel("183");
User u2 = new User();
u2.setId(1);
u2.setName("李四");
u2.setTel("183");
list.add(u1);
list.add(u2);
// 使用sort方法排序
Collections.sort(list, new MyComparator());
for (User user : list) {
System.out.println(user);
}
}
内部比较器实现:
// User类实现Comparable接口,并实现compareTo方法
@Data
public class User implements Comparable<User>{
private Integer id;
private String name;
private String tel;
@Override
public int compareTo(User o) {
// 根据id排序
int compare = Integer.compare(this.id, o.getId());
if (compare == 0) {
// 若id相同,则根据名字排序
return this.name.compareTo(o.getName());
}
return compare;
}
}
public static void main(String[] args){
List<User> list = new ArrayList<>();
User u1 = new User();
u1.setId(2);
u1.setName("张三");
u1.setTel("183");
User u2 = new User();
u2.setId(1);
u2.setName("李四");
u2.setTel("183");
list.add(u1);
list.add(u2);
// 使用sort方法排序
Collections.sort(list);
for (User user : list) {
System.out.println(user);
}
}
6、泛型(Generic Type)
泛型:一个待确定的类型(类型占位符),在设计时用泛型,在使用时,需要给定一个确定的类型,如果不明确指定,默认类型为Object
集合中用到泛型:泛型集合
普通的类中用到泛型:泛型类
方法中用到泛型:泛型方法
接口中用到泛型:泛型接口
泛型的优点:在使用时,会用语法检查,避免添加错误类型的数据
通配符
<?>:无界通配符
List<?> list = new ArrayList<a>();
a可以是任意类型
<? extends E>:上界通配符
List<? extends E> list = new ArrayList<a>();
此时a可以是E或者E的子类类型,参数列表中如果有多个类型参数上限,用逗号分开
< ? super E>:下界通配符
List<? super E> list = new ArrayList<a>();
此时a可以是E或者E的父类
泛型 ? 通配符与T作用
T 是一个 确定的类型,通常用于泛型类和泛型方法的定义,?是一个不确定 的类型,通常用于泛型方法的调用代码和形参,不能用于定义类和泛型方法。例如List<E>
表示集合里是E类的实例,List<?>
表示集合里的对象类型不确定,与List一样。
四、IO流与XML
1、IO概述
IO:分为:Input与Output
**Input:**输入,将磁盘中的文件读到内存中
**Output:**输出,将内存中的数据写入磁盘
**Stream:**流,将磁盘中的静态字节数据变成流动中的字节数据
文件进行读写的操作的所有接口与类放在:java.io
包中
2、File类
探测文件(文件夹)的信息,再进一步对文件(文件夹)进行操作
**File:**获取文件的信息、创建、删除、重命名、移动等操作
**对目录:**提供删除、创建目录以及获取目录下所有文件和文件夹等
常见方法
1.对文件
- getPath():获取文件路径
- getName():获取文件名
- length():获取文件大小
- createNewFile():创建一个文件
- renameTo():重命名
- isFile():判断是否为文件
2.对文件夹
- isDirectory():判断是否是文件夹
- listFiles():返回一个目录中的文件数组
- mkdir():创建一个文件夹
3.共有
- exists():是否存在
- delete():删除
- listRoots():返回根目录
例子:获取指定目录下的所有文件和文件夹
public static void get(String path){
File file = new File(path);
if (file.exists()) {
File[] files = file.listFiles();
for (File f : files) {
if (f.isDirectory()) {
System.out.println(f.getPath());
get(f.getPath());
} else {
System.out.println(f.getPath());
}
}
}
}
3、IO流
1.字节流
对文件进行读写操作最底层的,字节流可以对任意格式的文件进行操作
InputStream:读操作的字节流
read():读方法,一次可以从流中读一个字节,返回-1代表读完。
read(byte[] bytes):读方法,将字节读到byte数组中。
read(byte[] bytes, int index, int len):读方法:将字节读到byte数组中,指定起点和长度。
例子:从一个文件中读取数据
// read()方法
public static void readFile(String path){
FileInputStream in = null;
try {
File file = new File(path);
if (file.exists()) {
in = new FileInputStream(file);
int s;
while ((s = in.read()) != -1) {
// 将int类型的数字强转成char类型,会自动把数字转成字符
System.out.println((char)s);
}
}
// 关闭流,建议放在finally中
in.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
// read(byte[] bytes)方法
public static void readFileBytes(String path){
FileInputStream in = null;
try {
File file = new File(path);
if (file.exists()) {
in = new FileInputStream(file);
byte[] b = new byte[in.available()];
while (in.read(b) != -1) {
String s = new String(b, "UTF-8");
System.out.println(s);
}
}
// 关闭流,建议放在finally中
in.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
OutputStream:写操作的字节流
write():写方法,一次可以向文件中写一个字节
write(byte[] datas):写方法,可以向文件中写入一个字节数组
write(byte[] datas,int startindex,int len):写方法,可以向文件中写入字节数组中指定的字节
例子:向文件中写入数据
public static void write(String path){
OutputStream out = null;
try {
File file = new File(path);
if (file.exists()) {
String s = "hello,world";
byte[] bytes = s.getBytes("UTF-8");
// 为true表示往文件追加写入,为false表示将覆盖文件内已有的数据
out = new FileOutputStream(file, true);
out.write(bytes);
out.close();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
注意:流一定要关闭,而且最好放在finally块中,先使用的流后关闭,后使用的流先关闭。
2.字符流
对磁盘文本文件的读写,用字节流可以实现,但不太方便;SUN设计专业的一组流(字符流),对文本格式的文件进行读写操作,相对字节流,要方便一些;为了提供效率,再用上SUN设计的缓冲流。
可以通过字符流读文本格式文件,但不能用来读图片文件,声音文件,视频文件等。
**Reader:**读操作的字符流
**Writer:**写操作的字符流
方法与字节流类似
3.字符缓冲流
字符缓冲流只能对字符流进行缓冲 ,字符缓冲流有2个:BufferedReader , BufferedWriter
readLIne():读取一行的数据
newLine():换行
write():写入一行数据
4.其他流
1.标准输入输出流
System.in:获取键盘的数据
System.out:输出到控制台
2.转换流
将字节流转换为字符流(单向)
// 读的转换
InputStream in = new FileInputStream("D:/");
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
// 写的转换
OutputStream out = new FileOutputStream("D:/");
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));
3.对象流
对象流:将一个对象持久化
序列化:将对象转为流,用来存贮或者网络传输
反序列化:将流中的数据转为对象
**注:**一个类创建的对象要能够被序列化,此类必须实现Serializable接口
public class ObjectOutputStream extends OutputStream
public class ObjectInputStream extends InputStream
4.数据流
数据流:将基本数据类型的数据及字符串持久化;
public class DataInputStream extends FilterInputStream extends InputStream
public class DataOutputStream extends FilterOutoputStream extends OutputStream
4、XML
XML:Extensible Markup Language (可扩展的标签语言),xml是一个纯文本格式的文件,相对文本文件,XML文档的内容一般用标签包裹, XML在项目主要用来存放配制信息或SQL语句
XML文档作用 :存贮数据
XML文档编写规范:
- 第1行为文档声明,写法固定,一般不需手写,开发工具自动生成
- xml有且只一个根元素(节点)
- 标签(元素)需要用一对尖括号包裹起来。标签一般是成对出现的,起始标签与结束标签只差“/"
- xml区分大小写,一般用小写
- 元素可以有属性,多个属性需要用空格分开,属性值需要用双引号括起来,属性需要放到起始标签中
XML文档一般3 三种写法
<?xml version="1.0" encoding="UTF-8"?>
<users>
<user>
<name>张三</name>
<job>C程序员</job>
<nick>高级程序员</nick>
<email>zhang@qq.com</email>
</user>
</users>
<?xml version="1.0" encoding="UTF-8"?>
<users>
<user name="张一" job="菜鸟" nick="初级程序员" email="zhang@qq.com" />
<user name="张二" job="老鸟" nick="高级程序员" email="zhanger@qq.com"/>
</users>
<?xml version="1.0" encoding="UTF-8"?>
<users>
<user id="1">
<name>张一</name>
<job>程序员</job>
<nick>高级</nick>
<email>zhang@qq.com</email>
</user>
</users>
解析XML:在JDK中SUN设计了一组类与接口,用来解析XML文档。SUN设计的解析XML的组件用起来不太方便,所以在实际应用,一般采用第三方解析组件DOM4J
,具体可以网上查询相关资料
默认,没添加任意约束的XML文档的编写是随意的,标签名任意,标签包含的标签设计也是随意的,这样带来的最大缺点:不好解析,不能写一段程序来解析任意结构的XML文件,文档结构混乱的,所以就需要一个结构确定的XML文件给当前XML设计文档结构
XML的约束有2种:DTD和Schema,在这里就不介绍了,有兴趣的可以自己去专研
五、反射
1、反射
在程序运行时,可以根据给定的类动态的获取类的信息
通过反射方式使用此类 SUN设计的反射相关组件放在:java.lang.reflect
包
探测器对象(Class类型的对象):反射主要就是靠Class进行
获取Class:
// 方法一
User user = new User();
Class<?> c = user.getClass();
// 方法二
Class<?> clz2 = User.class;
// 方法三,className:类的全路径
Class<?> clz3 = Class.forName(className);
// 对基本数据类型的包装类
Class<?> clz4 = Integer.TYPE;
// 获取Class类的父类的Class对象,只能通过这种方式
Class<?> clz4 = String .class.getSuperClass();
2、类加载器(ClassLoader)
类加载器负责在运行时将 Java 类动态的加载到 JVM中 **,是 JRE的一部分。因此,借助类加载器,JVM 无需了解底层文件或文件系统即可运行 Java 程序。
类不会一次全部加载到内存中,而是在应用程序需要它们时才会进行加载。
类加载器分三种:
- Application ClassLoader:系统类加载器(应用类加载器),继承扩展类加载器,加载的是一个项目中bin文件夹里的文件,即开发者自己写的类
- Extension ClassLoader:扩展类加载器,继承引导类加载器,加载的是jdk目录下
jre/lib/ext
内的文件 - Bootstrap ClassLoader:引导类加载器,该加载器是内嵌在jvm中一段特殊的用来加载java核心类库的**C++**代码,引导类加载器不是java原生代码编写的,所以没有实例,为null,加载的是jdk目录下
jre/lib
中的rt.jar及其他重要文件
// 1、系统类加载器
ClassLoader loader1 = ClassLoader.getSystemClassLoader();
// 2、扩展类加载器
ClassLoader loader2 = loader1.getParent();
// 3、引导类加载器
ClassLoader loader3 = loader1.getParent(); //null
properties文件
- 该文件一般用于存放配置
- 格式为key-value键值对
- 一行放一个key-value键值对
应用:通过类加载器获取项目资源
ClassLoader loader1 = ClassLoader.getSystemClassLoader();
InputStream in = loader1.getResourceAsStream("db.properties");
//对properties文件,SUN设计了一个类:Properties类,直接解析此文件
Properties prop = new Properties();
prop.load(in);
//获取文件信息
String user = prop.getProperty("user");
String password = prop.getProperty("password");
System.out.println("user ->" + user);
System.out.println("password -> " + password);
3、获取类信息
1.获取类属性
拥有Field、Constructor和Method三种类型
public static void main(String[] args) throws Exception{
Class<?> c = Persion.class;
// 获取类的属性
Field[] fields = c.getDeclaredFields();
// 获取该属性所爱的类或接口的名称
System.out.println(fields[0].getDeclaringClass());
for (Field f : fields) {
System.out.println(String.format("%s %s %s;", Modifier.toString(f.getModifiers()), f.getType(), f.getName()));
}
// 获取类的构造方法
Constructor<?>[] constructors = c.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
// 获取类的成员方法
Method[] methods = c.getDeclaredMethods();
for (Method m : methods) {
System.out.println(m);
}
}
注:方法中带有Declared的为仅获取本类的类型,去掉Declared的方法为获取本类及其父类的类型
4、反射应用
应用 :创建对象、设置属性、调用方法
public static void main(String[] args) throws Exception{
Class<?> c = Persion.class;
// 创建对象
// 无参构造方法创建
Persion p1 = (Persion) c.newInstance();
Constructor<?> constructor = c.getDeclaredConstructor(int.class, String.class, String.class);
Persion p2 = (Persion) constructor.newInstance(1, "张三", "1333333333");
// 设置属性,设置上方的p1的name
Field field = c.getDeclaredField("name");
// 设置为true,使属性可访问,因为该属性是private的
field.setAccessible(true);
field.set(p1, "李四");
System.out.println(p1);
// 调用方法,调用p2的show方法
Method method = c.getDeclaredMethod("show", String.class);
Object result = method.invoke(p2, "张三");
System.out.println(result);
}
5、IOC原理
IOC:Inverstion Of Control:控制反转,将程序中对象创建的控制权由用户转交给Spring IOC容器,程序中需要用到哪此对象,直接从容器中取
容器中的对象:会在项目启动时,完成初使化
Spring IOC容器通过反射创建对象
任何一个框架,都有配制文件,框架在运行会读取配制,根据配制文件完成项目必须设制及初使化
所以IOC容器就是根据配制中要初使化的对象,在项目启动时,完成对象的创建
6、AOP原理
AOP:Aspect Oriented Programming :面向切面编程,实现的是对功能的扩展不需要通过修改源码实现,而是在程序运行中动态注入进来
将方法的内的核心业务与非核心业务分离,非核心业务切出去,非核心业务在程序运行时可以通过代理模式动态的植入进来
IOC和AOP为spring的内容,有兴趣的可以搜索相关资料