Java数据结构
Java工具包提供了强大的数据结构。在Java中的数据结构主要包括以下几种接口和类:
- 枚举(Enumeration)
- 位集合(BitSet)
- 向量(Vector)
- 栈(Stack)
- 字典(Dictionary)
- 哈希表(Hashtable)
- 属性(Properties)
枚举(Enumeration)
枚举(Enumeration)接口虽然它本身不属于数据结构,但它在其他数据结构的范畴里应用很广。 枚举(The Enumeration)接口定义了一种从数据结构中取回连续元素的方式。
例如,枚举定义了一个叫nextElement 的方法,该方法用来得到一个包含多元素的数据结构的下一个元素。
位集合(BitSet)
位集合类实现了一组可以单独设置和清除的位或标志。
该类在处理一组布尔值的时候非常有用,你只需要给每个值赋值一"位",然后对位进行适当的设置或清除,就可以对布尔值进行操作了。
向量(Vector)
向量(Vector)类和传统数组非常相似,但是Vector的大小能根据需要动态的变化。
和数组一样,Vector对象的元素也能通过索引访问。
使用Vector类最主要的好处就是在创建对象的时候不必给对象指定大小,它的大小会根据需要动态的变化。
栈(Stack)
栈(Stack)实现了一个后进先出(LIFO)的数据结构。
你可以把栈理解为对象的垂直分布的栈,当你添加一个新元素时,就将新元素放在其他元素的顶部。
当你从栈中取元素的时候,就从栈顶取一个元素。换句话说,最后进栈的元素最先被取出。
字典(Dictionary)
字典(Dictionary) 类是一个抽象类,它定义了键映射到值的数据结构。
当你想要通过特定的键而不是整数索引来访问数据的时候,这时候应该使用Dictionary。
由于Dictionary类是抽象类,所以它只提供了键映射到值的数据结构,而没有提供特定的实现。
哈希表(Hashtable)
Hashtable类提供了一种在用户定义键结构的基础上来组织数据的手段。
例如,在地址列表的哈希表中,你可以根据邮政编码作为键来存储和排序数据,而不是通过人名。
哈希表键的具体含义完全取决于哈希表的使用情景和它包含的数据。
属性(Properties)
Properties 继承于 Hashtable.Properties 类表示了一个持久的属性集.属性列表中每个键及其对应值都是一个字符串。
Properties 类被许多Java类使用。例如,在获取环境变量时它就作为System.getProperties()方法的返回值。
Java集合
集合框架图
集合接口
集合框架定义了一些接口。本节提供了每个接口的概述:
接口 | 描述 |
---|---|
Collection | Collection 是最基本的集合接口,一个 Collection 代表一组 Object,即 Collection 的元素, Java不提供直接继承自Collection的类,只提供继承于的子接口(如List和set) Collection 接口存储一组不唯一,无序的对象 |
List | List接口是一个有序的 Collection,使用此接口能够精确的控制每个元素插入的位置,能够通过索引(元素在List中位置,类似于数组的下标)来访问List中的元素,第一个元素的索引为 0,而且允许有相同的元素。 List 接口存储一组不唯一,有序(插入顺序)的对象。 |
Set | Set 具有与 Collection 完全一样的接口,只是行为上不同,Set 不保存重复的元素。Set 接口存储一组唯一,无序的对象。 |
SortedSet | 继承于Set保存有序的集合。 |
Map | Map 接口存储一组键值对象,提供key(键)到value(值)的映射 |
Map.Entry | 描述在一个Map中的一个元素(键/值对)。是一个 Map 的内部接口。 |
SortedMap | 继承于 Map,使 Key 保持在升序排列。 |
Set和List的区别
- Set 接口实例存储的是无序的,不重复的数据。List 接口实例存储的是有序的,可以重复的元素。
- Set 检索效率低下,删除和插入效率高,插入和删除不会引起元素位置改变 实现类有HashSet,TreeSet。
- List 和数组类似,可以动态增长,根据实际存储的数据的长度自动增长 List 的长度。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变。实现类有ArrayList,LinkedList,Vector。
集合实现类(集合类)
Java提供了一套实现了Collection接口的标准集合类。其中一些是具体类,这些类可以直接拿来使用,而另外一些是抽象类,提供了接口的部分实现。
类 | 描述 |
---|---|
AbstractCollection | 实现了大部分的集合接口。 |
AbstractList | 继承于AbstractCollection 并且实现了大部分List接口。 |
AbstractSequentialList | 继承于 AbstractList ,提供了对数据元素的链式访问而不是随机访问。 |
LinkedList | 该类实现了List接口,允许有null(空)元素。主要用于创建链表数据结构,该类没有同步方法,如果多个线程同时访问一个List,则必须自己实现访问同步,解决方法就是在创建List时候构造一个同步的List。LinkedList 查找效率低。 |
ArrayList | 该类也是实现了List的接口,实现了可变大小的数组,随机访问和遍历元素时,提供更好的性能。该类也是非同步的,在多线程的情况下不要使用。ArrayList 增长当前长度的50%,插入删除效率低。 |
AbstractSet | 继承于AbstractCollection 并且实现了大部分Set接口。 |
HashSet | 该类实现了Set接口,不允许出现重复元素,不保证集合中元素的顺序,允许包含值为null的元素,但最多只能一个 |
LinkedHashSet | 具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。 |
TreeSet | 该类实现了Set接口,可以实现排序等功能。 |
AbstractMap | 实现了大部分的Map接口。 |
HashMap | HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。该类实现了Map接口,根据键的HashCode值存储数据,具有很快的访问速度,最多允许一条记录的键为null,不支持线程同步。 |
TreeMap | 继承了AbstractMap,并且使用一颗树。 |
WeakHashMap | 继承AbstractMap类,使用弱密钥的哈希表。 |
LinkedHashMap | 继承于HashMap,使用元素的自然顺序对元素进行排序. |
IdentityHashMap | 继承AbstractMap类,比较文档时使用引用相等。 |
util包中定义的类
类 | 描述 |
---|---|
Vector | 该类和ArrayList非常相似,但是该类是同步的,可以用在多线程的情况,该类允许设置默认的增长长度,默认扩容方式为原来的2倍。 |
Stack | 栈是Vector的一个子类,它实现了一个标准的后进先出的栈。 |
Dictionary | Dictionary 类是一个抽象类,用来存储键/值对,作用和Map类相似。 |
Hashtable | Hashtable 是 Dictionary(字典) 类的子类,位于 java.util 包中。 |
Properties | Properties 继承于 Hashtable,表示一个持久的属性集,属性列表中每个键及其对应值都是一个字符串。 |
BitSet | 一个Bitset类创建一种特殊类型的数组来保存位值。BitSet中数组大小会随需要增加。 |
迭代器
Java Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法,可用于迭代集合
-
迭代器 的两个基本操作是 next 、hasNext 和 remove。
调用 next() 会返回迭代器的下一个元素,并且更新迭代器的状态。
调用 hasNext() 用于检测集合中是否还有元素。
调用 remove() 将迭代器返回的元素删除。
-
遍历ArrayList
import java.util.*;
public class Test{
public static void main(String[] args) {
List<String> list=new ArrayList<String>();
list.add("Hello");
list.add("World");
list.add("HAHAHAHA");
//第一种遍历方法使用 For-Each 遍历 List
for (String str : list) {
//也可以改写 for(int i=0;i<list.size();i++) 这种形式
System.out.println(str);
}
//第二种遍历,把链表变为数组相关的内容进行遍历
String[] strArray=new String[list.size()];
list.toArray(strArray);
for(int i=0;i<strArray.length;i++) //这里也可以改写为 for(String str:strArray) 这种形式
{
System.out.println(strArray[i]);
}
//第三种遍历 使用迭代器进行相关遍历
Iterator<String> ite=list.iterator();
while(ite.hasNext())//判断下一个元素之后有值
{
System.out.println(ite.next());
}
}
三种方法都是用来遍历ArrayList集合,第三种方法是采用迭代器的方法,该方法可以不用担心在遍历的过程中会超出集合的长度。
- 遍历 Map
import java.util.*;
public class Test{
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("1", "value1");
map.put("2", "value2");
map.put("3", "value3");
//第一种:普遍使用,二次取值
System.out.println("通过Map.keySet遍历key和value:");
for (String key : map.keySet()) {
System.out.println("key= "+ key + " and value= " + map.get(key));
}
//第二种
System.out.println("通过Map.entrySet使用iterator遍历key和value:");
Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> entry = it.next();
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
//第三种:推荐,尤其是容量大时
System.out.println("通过Map.entrySet遍历key和value");
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
//第四种
System.out.println("通过Map.values()遍历所有的value,但不能遍历key");
for (String v : map.values()) {
System.out.println("value= " + v);
}
}
}
- for循环、增强for循环和迭代器的区别
- 迭代器是用于方便集合遍历的,实现了Iterable接口的集合都可以使用迭代器来遍历。使用迭代器遍历元素时,除了查看之外,只能做remove操作。
- 增强for循环,内部使用的是迭代器,所以它的操作对象是数组和可以使用迭代器的集合。遍历时只能查看,无法修改、删除、增加。
- 所以如果需要对遍历的对象做增删修改的操作,使用普通的for循环来操作。
ArrayList
- ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。
- ArrayList 继承了 AbstractList ,并实现了 List 接口。
ArrayList 类位于 java.util 包中,使用前需要引入它,语法格式如下:
常用方法
import java.util.ArrayList; // 引入 ArrayList 类
ArrayList<E> objectName =new ArrayList<>(); // 初始化
方法 | 描述 |
---|---|
add( | 将元素插入到指定位置的 arraylist 中 |
addAll() | 添加集合中的所有元素到 arraylist 中 |
clear() | 删除 arraylist 中的所有元素 |
clone() | 复制一份 arraylist |
contains() | 判断元素是否在 arraylist |
get() | 通过索引值获取 arraylist 中的元素 |
indexOf() | 返回 arraylist 中元素的索引值 |
removeAll() | 删除存在于指定集合中的 arraylist 里的所有元素 |
remove()) | 删除 arraylist 里的单个元素 |
size() | 返回 arraylist 里元素数量 |
isEmpty() | 判断 arraylist 是否为空 |
subList() | 截取部分 arraylist 的元素 |
set() | 替换 arraylist 中指定索引的元素 |
sort() | 对 arraylist 元素进行排序 |
toArray() | 将 arraylist 转换为数组 |
toString() | 将 arraylist 转换为字符串 |
ensureCapacity | 设置指定容量大小的 arraylist |
lastIndexOf() | 返回指定元素在 arraylist 中最后一次出现的位置 |
retainAll() | 保留 arraylist 中在指定集合中也存在的那些元素 |
containsAll() | 查看 arraylist 是否包含指定集合中的所有元素 |
trimToSize() | 将 arraylist 中的容量调整为数组中的元素个数 |
removeRange() | 删除 arraylist 中指定索引之间存在的元素 |
replaceAll() | 将给定的操作内容替换掉数组中每一个元素 |
removeIf() | 删除所有满足特定条件的 arraylist 元素 |
forEach() | 遍历 arraylist 中每一个元素并执行特定操作 |
LinkedList
链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的地址。
- Java LinkedList(链表) 类似于 ArrayList,是一种常用的数据容器。
- 与 ArrayList 相比,LinkedList 的增加和删除的操作效率更高,而查找和修改的操作效率较低。
以下情况使用 ArrayList :
- 频繁访问列表中的某一个元素。
- 只需要在列表末尾进行添加和删除元素操作。
以下情况使用 LinkedList :
- 你需要通过循环迭代来访问列表中的某些元素。
- 需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作。
LinkedList 继承了 AbstractSequentialList 类。
LinkedList 实现了 Queue 接口,可作为队列使用。
LinkedList 实现了 List 接口,可进行列表的相关操作。
LinkedList 实现了 Deque 接口,可作为队列使用。
LinkedList 实现了 Cloneable 接口,可实现克隆。
LinkedList 实现了 java.io.Serializable 接口,即可支持序列化,能通过序列化去传输。
###常用方法
方法 | 描述 |
---|---|
public boolean add(E e) | 链表末尾添加元素,返回是否成功,成功为 true,失败为 false。 |
public void add(int index, E element) | 向指定位置插入元素。 |
public boolean addAll(Collection c) | 将一个集合的所有元素添加到链表后面,返回是否成功,成功为 true,失败为 false。 |
public boolean addAll(int index, Collection c) | 将一个集合的所有元素添加到链表的指定位置后面,返回是否成功,成功为 true,失败为 false。 |
public void addFirst(E e) | 元素添加到头部。 |
public void addLast(E e) | 元素添加到尾部。 |
public boolean offer(E e) | 向链表末尾添加元素,返回是否成功,成功为 true,失败为 false。 |
public boolean offerFirst(E e) | 头部插入元素,返回是否成功,成功为 true,失败为 false。 |
public boolean offerLast(E e) | 尾部插入元素,返回是否成功,成功为 true,失败为 false。 |
public void clear() | 清空链表。 |
public E removeFirst() | 删除并返回第一个元素。 |
public E removeLast() | 删除并返回最后一个元素。 |
public boolean remove(Object o) | 删除某一元素,返回是否成功,成功为 true,失败为 false。 |
public E remove(int index) | 删除指定位置的元素。 |
public E poll() | 删除并返回第一个元素。 |
public E remove() | 删除并返回第一个元素。 |
public boolean contains(Object o) | 判断是否含有某一元素。 |
public E get(int index) | 返回指定位置的元素。 |
public E getFirst() | 返回第一个元素。 |
public E getLast() | 返回最后一个元素。 |
public int indexOf(Object o) | 查找指定元素从前往后第一次出现的索引。 |
public int lastIndexOf(Object o) | 查找指定元素最后一次出现的索引。 |
public E peek() | 返回第一个元素。 |
public E element() | 返回第一个元素。 |
public E peekFirst() | 返回头部元素。 |
public E peekLast() | 返回尾部元素。 |
public E set(int index, E element) | 设置指定位置的元素。 |
public Object clone() | 克隆该列表。 |
public Iterator descendingIterator() | 返回倒序迭代器。 |
public int size() | 返回链表元素个数。 |
public ListIterator listIterator(int index) | 返回从指定位置开始到末尾的迭代器。 |
public Object[] toArray() | 返回一个由链表元素组成的数组。 |
HashSet
- HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。
- HashSet 允许有 null 值。
- HashSet 是无序的,即不会记录插入的顺序。
- HashSet 不是线程安全的, 如果多个线程尝试同时修改 HashSet,则最终结果是不确定的。 您必须在多线程访问时显式同步对 HashSet 的并发访问。
- HashSet 实现了 Set 接口。
##HashMap
- HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。
- HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步。
- HashMap 是无序的,即不会记录插入的顺序。
- HashMap 继承于AbstractMap,实现了 Map、Cloneable、java.io.Serializable 接口。
- HashMap 的 key 与 value 类型可以相同也可以不同,可以是字符串(String)类型的 key 和 value,也可以是整型(Integer)的 key 和字符串(String)类型的 value。
常用方法
方法 | 描述 |
---|---|
clear() | 删除 hashMap 中的所有键/值对 |
clone() | 复制一份 hashMap |
isEmpty() | 判断 hashMap 是否为空 |
size() | 计算 hashMap 中键/值对的数量 |
put() | 将键/值对添加到 hashMap 中 |
putAll() | 将所有键/值对添加到 hashMap 中 |
putIfAbsent() | 如果 hashMap 中不存在指定的键,则将指定的键/值对插入到 hashMap 中。 |
remove() | 删除 hashMap 中指定键 key 的映射关系 |
containsKey() | 检查 hashMap 中是否存在指定的 key 对应的映射关系。 |
containsValue() | 检查 hashMap 中是否存在指定的 value 对应的映射关系。 |
replace() | 替换 hashMap 中是指定的 key 对应的 value。 |
replaceAll() | 将 hashMap 中的所有映射关系替换成给定的函数所执行的结果。 |
get() | 获取指定 key 对应对 value |
getOrDefault() | 获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值 |
forEach() | 对 hashMap 中的每个映射执行指定的操作。 |
entrySet() | 返回 hashMap 中所有映射项的集合集合视图。 |
keySet() | 返回 hashMap 中所有 key 组成的集合视图。 |
values() | 返回 hashMap 中存在的所有 value 值。 |
merge() | 添加键值对到 hashMap 中 |
compute() | 对 hashMap 中指定 key 的值进行重新计算 |
computeIfAbsent() | 对 hashMap 中指定 key 的值进行重新计算,如果不存在这个 key,则添加到 hasMap 中 |
computeIfPresent() | 对 hashMap 中指定 key 的值进行重新计算,前提是该 key 存在于 hashMap 中。 |
#JavaIO流
在 Java 程序中,对于数据的输入/输出操作以**流(Stream)**方式进行
JDK 提供了各种各样的流类,用以获取不同种类的数据
程序中通过标准的方法输入/输出数据
Java.io 包几乎包含了所有操作输入、输出需要的类。所有这些流类代表了输入源和输出目标。
Java.io 包中的流支持很多种格式,比如:基本类型、对象、本地化字符集等等。
一个流可以理解为一个数据的序列。输入流表示从一个源读取数据,输出流表示向一个目标写数据。
Java 为 I/O 提供了强大的而灵活的支持,使其更广泛地应用到文件传输和网络编程中。
结构图
文件
Java 有一个类叫 File,它封装的是文件的文件名,只是内存里面的一个对象
真正的文件是在硬盘上的一块空间,在这个文件里面存放着各种各样的数据
读取文件是通过流的方式来读
对于计算机来说,无论读什么类型的数据,都是以 01 的形式读取的。
可以把文件想象成一个桶,文件里面的数据相当于桶里的水,从桶里取水就是从文件读取数据。
常见的取水方法是用一根管道插到桶里面,在管道另一边打开水龙头,桶里的水就从水龙头里流出来了。
桶里的水是通过这根管道流出来的,因此这根管道就叫流。
Java 的流式输入/输出跟水流的原理是一样的。
当要从文件读取数据时
管道一头连着接收方,一头连着文件
文件里的数据顺着管道流出来。
接收方读取从文件流出来的数据。
当要往文件写入数据时
管道一头连着输入方,一头连着文件
输入方的数据顺着管道流进去。
文件处写入管道流出的数据。
网络
除了从文件读取数据,还可以通过网络
用一根管道把两个机子连接起来。
一边说一句话,通过这个管道流进另一边,反之亦然。
包装
有的时候,一根管道不够用
比如这根管道流过来的水有杂质。
可以在这根管道外面再包一层管道,把杂质给过滤掉。
从程序的角度来讲,从计算机读取到的原始数据都是 01 这种形式的,一个字节一个字节地往外读
这样会有些不合适,比如读取字符的时候,由于一个字符占两个字节,按字节读就可能存在只读了半个字符的情况,这样就会显示为乱码。
这时可以在这根管道的外面再包一层比较强大的管道,这个管道可以把 01 转换成字符串。
这样使用程序读取数据时读到的就不再是 01 这种形式的数据了,而是可以看得懂的字符串。
##分类
可以从不同的角度对其进行分类:
-
按数据流的方向不同可以分为输入流和输出流
-
按照处理数据单位不同可以分为字节流和字符流
-
按照功能不同可以分为节点流和处理流
-
字节流:最原始的流,读出来的数据就是 01 格式,只不过它是按照字节来读的,一个字节(Byte)是 8 位(bit),读的时候不是一个位一个位读,而是一个字节一个字节读。
-
字符流:一个字符一个字符地往外读取数据。一个字符是2个字节。
JDK 所提供的所有流类型位于包 java.io内,分别继承自以下四种抽象流类型:
- 输入流:InPutStream(字节流),Reader(字符流)
- 输出流:OutPutStream(字节流),Writer(字符流)
管道一端文件里,一端程序里,然后开始读数据。
如果站在文件的角度上,这叫输出。
如果站在程序的角度上,这叫输入。
IO 流的输入输出都是站在程序的角度上的。
相关功能
###读取控制台输入
Java 的控制台输入由 System.in 完成。
为了获得一个绑定到控制台的字符流,你可以把 System.in 包装在一个 BufferedReader 对象中来创建一个字符流。
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedReader 对象创建后,我们便可以使用 read() 方法从控制台读取一个字符,或者用 readLine() 方法读取一个字符串。
###控制台读取多字符输入
从 BufferedReader 对象读取一个字符要使用 read() 方法,它的语法如下:
import java.io.*;
public class BRRead {
public static void main(String[] args) throws IOException {
char c;
// 使用 System.in 创建 BufferedReader
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("输入字符, 按下 'q' 键退出。");
// 读取字符
do {
c = (char) br.read();
System.out.println(c);
} while (c != 'q');
}
}
###控制台读取字符串
从标准输入读取一个字符串需要使用 BufferedReader 的 readLine() 方法。
import java.io.*;
public class BRReadLines {
public static void main(String[] args) throws IOException {
// 使用 System.in 创建 BufferedReader
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String str;
System.out.println("Enter lines of text.");
System.out.println("Enter 'end' to quit.");
do {
str = br.readLine();
System.out.println(str);
} while (!str.equals("end"));
}
}
控制台输出
import java.io.*;
//演示 System.out.write().
public class WriteDemo {
public static void main(String[] args) {
int b;
b = 'A';
System.out.write(b);
System.out.write('\n');
}
}
流的方法
FileInputStream
该流用于从文件读取数据,它的对象可以用关键字 new 来创建。
有多种构造方法可用来创建对象。
方法 | 描述 |
---|---|
public void close() throws IOException{} | 关闭此文件输入流并释放与此流有关的所有系统资源。抛出IOException异常。 |
protected void finalize()throws IOException {} | 这个方法清除与该文件的连接。确保在不再引用文件输入流时调用其 close 方法。抛出IOException异常。 |
public int read(int r)throws IOException{} | 这个方法从 InputStream 对象读取指定字节的数据。返回为整数值。返回下一字节数据,如果已经到结尾则返回-1。 |
public int read(byte[] r) throws IOException{} | 这个方法从输入流读取r.length长度的字节。返回读取的字节数。如果是文件结尾则返回-1。 |
public int available() throws IOException{} | 返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取的字节数。返回一个整数值。 |
###FileOutputStream
该类用来创建一个文件并向文件中写数据。
如果该流在打开文件进行输出前,目标文件不存在,那么该流会创建该文件。
方法 | 描述 |
---|---|
public void close() throws IOException{} | 关闭此文件输入流并释放与此流有关的所有系统资源。抛出IOException异常。 |
protected void finalize()throws IOException {} | 这个方法清除与该文件的连接。确保在不再引用文件输入流时调用其 close 方法。抛出IOException异常。 |
public void write(int w)throws IOException{} | 这个方法把指定的字节写到输出流中。 |
public void write(byte[] w) | 把指定数组中w.length长度的字节写到OutputStream中。 |
文件I/O
File类中有两个方法可以用来创建文件夹:
- **mkdir( )**方法创建一个文件夹,成功则返回true,失败则返回false。失败表明File对象指定的路径已经存在,或者由于整个路径还不存在,该文件夹不能被创建。
- **mkdirs()**方法创建一个文件夹和它的所有父文件夹。
import java.io.File;
public class CreateDir {
public static void main(String[] args) {
String dirname = "/tmp/user/java/bin";
File d = new File(dirname);
// 现在创建目录
d.mkdirs();
}
}
读取目录
import java.io.File;
public class DirList {
public static void main(String args[]) {
String dirname = "/tmp";
File f1 = new File(dirname);
if (f1.isDirectory()) {
System.out.println("目录 " + dirname);
String s[] = f1.list();
for (int i = 0; i < s.length; i++) {
File f = new File(dirname + "/" + s[i]);
if (f.isDirectory()) {
System.out.println(s[i] + " 是一个目录");
} else {
System.out.println(s[i] + " 是一个文件");
}
}
} else {
System.out.println(dirname + " 不是一个目录");
}
}
}
删除目录或文件
import java.io.File;
public class DeleteFileDemo {
public static void main(String[] args) {
// 这里修改为自己的测试目录
File folder = new File("/tmp/java/");
deleteFolder(folder);
}
// 删除文件及目录
public static void deleteFolder(File folder) {
File[] files = folder.listFiles();
if (files != null) {
for (File f : files) {
if (f.isDirectory()) {
deleteFolder(f);
} else {
f.delete();
}
}
}
folder.delete();
}
}
Java多线程
生命周期
-
新建状态:
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
-
就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
-
运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
-
阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
- 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
- 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
- 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
-
死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
线程的优先级
- 线程优先级越高,权重越高,越"容易"优先执行
- Thread.getPriority() 获取优先级
- Thread.setPriority() 设置优先级
- 优先级的范围是从1~10;最小是1,最大是10;寻常优先级为5;
创建线程
- 继承 Thread 类【不建议使用:避免OOP单继承局限性】
- 类继承Thread
- 重写run()
- start() 调用
- 实现 Runable 接口【推荐】
- 类实现Runnable 接口
- 重写run()
- start() 调用
- 实现 Callable 接口
- 实现 Callable 接口, 需要返回值
- 重写 call 方法,需要抛出异常
- 创建目标对象
- 创建执行服务3个: ExecutorService service = Executors.newCachedThreadPool(3);
- 提交执行:Future<> submit = service. submit (对象);
- 获取结果 :Boolean aBoolean = submit. get ();
- 关闭服务:service. shutdownNow ();
Thread方法
方法 | 描述 |
---|---|
public void start() | 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。 |
public void run() | 如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。 |
public final void setName(String name) | 改变线程名称,使之与参数 name 相同。 |
public final void setPriority(int priority) | 更改线程的优先级。 |
public final void setDaemon(boolean on) | 将该线程标记为守护线程或用户线程。 |
public final void join(long millisec) | 等待该线程终止的时间最长为 millis 毫秒。 |
public void interrupt() | 中断线程。 |
public final boolean isAlive() | 测试线程是否处于活动状态。 |
##Thread 类的静态方法
方法 | 描述 |
---|---|
public static void yield() | 暂停当前正在执行的线程对象,并执行其他线程。 |
public static void sleep(long millisec) | 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。 |
public static boolean holdsLock(Object x) | 当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。 |
public static Thread currentThread() | 返回对当前正在执行的线程对象的引用。 |
public static void dumpStack() | 将当前线程的堆栈跟踪打印至标准错误流。 |
- 创建线程的三种方式的对比
- 采用实现 Runnable、Callable 接口的方式创建多线程时,线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类。
- 使用继承 Thread 类的方式创建多线程时,编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread() 方法,直接使用 this 即可获得当前线程。
守护线程 daemon
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
- 守护线程有后台记录操作日志、监控内存、垃圾回收等待
- 设置守护线程Thread.setDaemon(true);默认为false;
线程同步 Synchronized
- Synchronized 方法控制对对象的访问,每个对象对应一把锁,每个 synchronized 方法都必须获取调用方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,直到方法返回才释放锁,后面被阻塞的线程才能获得锁,继续执行;
- synchronized 修饰的方法只能一个对象调用完之后,另一个对象才能调用。锁的是调用该方法的对象。默认锁的this
- synchronized是隐式锁,出了作用域自动释放。
死锁
-
多个线程互相抱着对方需要的资源,然后形成僵持。
同时拥有两个以上对象的锁,就可能发生死锁问题。
产生死锁的四个必要条件:- 互斥条件:一个资源只能被一个进程调用。
- 请求与保持条件:一个进程因请求资源而阻塞时,对方获得的资源保持不放。
- 不剥夺条件:进程已获得资源,在未使用完之前不能强行剥夺。
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源状态。
-
lock
-
可重入锁 ReentrantLock
ReentrantLock类实现了Lock,比较常用,性能更高,扩展性好,提供更多的子类,手动开启和关闭
private final ReentrantLock lock = new ReentrantLock();
public void m(){
lock.lock();//启动锁
try{
//保证线程安全的代码;
}
finally{
lock.unlock();
//如果同步代码有异常,要讲unlock()写入finally语句块。
}
}
线程通信
方法 | 作用 |
---|---|
wait () | 表示线程一直等待,直到其他线程通知,与 sleep 不同,会释放锁 |
wait (long timeOut) | 指定等待的毫秒数 |
notify () | 唤醒一个处于等待状态的线程 |
notifyAll() | 唤醒同一个对象所有的调用 wait()方法的线程,优先级高的优先调度 |
obj.wait() | 表示线程一直等待,知道其他线程通知,与sleep不同的是,会释放锁 |
public synchronized void pushTo(Chicken chicken){
//如果容器满了,就需要等待消费者消费
if(chickens.length==count){
//通知消费者消费
try {
this.wait();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果没有满就丢入产品
chickens[count]=chicken;
count++;
//通知消费者消费
this.notifyAll();
}
线程池
- 可以提前创建好几个线程,放入线程池中,使用的时候,直接获取,使用完放回池中,可以避免频繁创建销毁线程,实现重复利用。
public static void main( String[] args) {
//1。创建服务,创建线程池
// newFixedThreadPool 参数为:线程池大小
ExecutorService service = Executors.newFixedThreadPool(10);
// 执行
service.execute (new MyThread());
service.execute (new MyThread());
service.execute (new MyThread());
service.execute (new MyThread());
//2.关闭链接
service.shutdown();
}
GUI编程
简介
Gui的核心技术: Swing AWT
- 界面不美观
- 需要jre环境
为什么要学习?
- 可以写出自己心中想要的一些小工具
- 工作时候,也可能维护到swing界面,但概率极小。
- 了解MVC架构,了解监听!
组件
- 窗口
- 弹窗
- 面板
- 文本框
- 列表框
- 按钮
- 图片
- 监听事件
- 鼠标
AWT
AWT介绍
- 包含了很多类和接口!GUI:图像用户界面。 Eeclipse:java环境写的
- 元素:窗口、按钮、文本框
- java.awt包里
组件和容器
- Frame
import java.awt.*;
//GUI的第一个界面
public class TestFrame {
public static void main(String[] args) {
//Frame,JDK 看源码;
Frame frame = new Frame("我的第一个java图形界面窗口");
//设置可见性 w h 没有
frame.setVisible(true);
//设置窗口大小
frame.setSize(400, 400);
//背景颜色 Color
Color color = new Color(155, 89, 104);
frame.setBackground(color);
//弹出的初始位置
frame.setLocation(200, 200);
//设置大小固定
frame.setResizable(false);
}
}
问题:窗口无法关闭—》java停止运行
尝试回顾封装:
import java.awt.*;
public class TestFrame2 {
public static void main(String[] args) {
MyFrame myFrame1 = new MyFrame(100, 100, 200, 200, Color.blue);
MyFrame myFrame2 = new MyFrame(300, 100, 200, 200, Color.yellow);
MyFrame myFrame3 = new MyFrame(500, 100, 200, 200, Color.RED);
MyFrame myFrame4 = new MyFrame(700, 100, 200, 200, Color.BLACK);
}
}
class MyFrame extends Frame{
//除了Frame方法以外,还有自己的方法
static int id = 0;//可能存在多个窗口,我们需要一个计数器
//构造器封装一下
public MyFrame(int x,int y, int w,int h,Color color){
super("MyFrame+"+(++id));
setBackground(color);
setVisible(true);
//相当于初始位置x,y和窗口大小w h
setBounds(x,y,w,h);
setResizable(false);
}
}
- 面板panel
解决了无法关闭问题,即调用addWindowsListener方法的子方法,并重写其中的WindowsClosing方法,来调用程序关闭的.exit(0)方法
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
//Panel 可以看成一个空间,但是不能单独存在,得放在Frame上
public class TsetPanel {
public static void main(String[] args) {
//窗口
Frame frame = new Frame();
//布局的概念
//面板
Panel panel = new Panel();
/*设置布局 不设置会默认置顶
未设置Layout时,java默认为flowLayout布局的,
设置为null即为清空布局管理器,之后添加组件,常常是设置组件左上角坐标相
对于容器左上角(0,0)的x,y值来确定组件的位置,即使更改容器大小也不会
改变位置。这种方式常常用于窗体大小固定的容器里。*/
frame.setLayout(null);
//坐标
frame.setBounds(300,300,500,500);
frame.setBackground(new Color(59, 164, 125));
//panel 设置坐标,相对于Frame的坐标
panel.setBounds(50,50,200,200);
panel.setBackground(new Color(90, 46, 30));
//frame.add(panel)frame添加面板
frame.add(panel);//Panel经过三层继承,最终继承了Component
frame.setVisible(true);
//监听时间,监听窗口关闭事件 System.exit(0)
//适配器模式: new 重写的太多了 new其子类 本来new WindowsListener的,但是要重写的实在太多了
frame.addWindowListener(new WindowAdapter() {
//窗口点击关闭的时候需要做的事情
@Override
public void windowClosing(WindowEvent e) {
//结束程序
System.exit(0);
}
});
}
}
- 布局管理器
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class TsetFlowLayout {
public static void main(String[] args) {
Frame frame = new Frame();
//组件-按钮
Button button1 = new Button("button1");
Button button2 = new Button("button2");
Button button3 = new Button("button3");
//设置为流式布局
//frame.setLayout(new FlowLayout());默认是中
//frame.setLayout(new FlowLayout(FlowLayout.LEFT));
frame.setLayout(new FlowLayout(FlowLayout.RIGHT));
frame.setSize(200,200);
frame.setVisible(true);
frame.add(button1);
frame.add(button2);
frame.add(button3);
frame.addWindowListener(new WindowAdapter() {
//窗口点击关闭的时候需要做的事情
@Override
public void windowClosing(WindowEvent e) {
//结束程序
System.exit(0);
}
});
}
}
- 东西南北中
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class TestBorderLayout {
public static void main(String[] args) {
Frame frame = new Frame("TestBorderLayout");
Button east = new Button("East");
Button west = new Button("West");
Button south = new Button("South");
Button north = new Button("North");
Button center = new Button("Center");
frame.add(east,BorderLayout.EAST);
frame.add(west ,BorderLayout.WEST);
frame.add(south ,BorderLayout.SOUTH);
frame.add(north ,BorderLayout.NORTH);
frame.add(center,BorderLayout.CENTER);
frame.setSize(300,300);
frame.setVisible(true);
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
}
- 表格布局三行两列这种Grid
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class TestGridLayout {
public static void main(String[] args) {
Frame frame = new Frame("TsetGridLayout");
Button button1 = new Button("button1");
Button button2 = new Button("button2");
Button button3 = new Button("button3");
Button button4 = new Button("button4");
Button button5 = new Button("button5");
Button button6 = new Button("button6");
frame.setLayout(new GridLayout(3,2));
frame.add(button1);
frame.add(button2);
frame.add(button3);
frame.add(button4);
frame.add(button5);
frame.add(button6);
frame.pack();//让布局变得好看
frame.setVisible(true);
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
}
知识总结
- Frame是一个顶级窗口
- Panel无法单独显示,必须添加到某个容器中
- 布局管理器
- 流失
- 东西南北中
- 表格
- 标题,大小,定位,背景颜色,可见性,监听
事件监听
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class TestActionEvent {
public static void main(String[] args) {
//按下按钮,触发一些事件
Frame frame = new Frame();
Button button1 = new Button("button1");
//因为addActionListener需要ActionListener,因此我们需要构造一个ActionListener
MyActionListener myActionListener = new MyActionListener();
button1.addActionListener(new MyActionListener());
frame.add(button1, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
windowsClosing(frame);
}
//关闭窗体的事件
private static void windowsClosing(Frame frame){
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
//事件监听
static class MyActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("11");
}
}
}
- 多个按钮共享一个事件
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileOutputStream;
public class TestActionEvent2 {
public static void main(String[] args) {
//两个按钮,实现同一个监听
Frame frame = new Frame("1111");
Button button1 = new Button("start");
Button button2 = new Button("stop");
//可以显示的定义出发会返回的命令,如果不显示定义,则会走默认值
//可以多个按钮只写一个监听类
button1.setActionCommand("3333");
My my = new My();
button1.addActionListener(my);
button2.addActionListener(my);
frame.add(button1, BorderLayout.WEST);
frame.add(button2, BorderLayout.EAST);
frame.pack();
frame.setVisible(true);
}
static class My implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
//e.getActionCommand()获得按钮的信息
System.out.println("按钮被点击了:msg" + e.getActionCommand());
}
}
}
- 输入框事件监听TextField
输入框中输入的字,可以打印出来,并将输入的字全部删除。
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class TestText01 {
public static void main(String[] args) {
//启动!只负责启动
new MyFrame();
}
}
class MyFrame extends Frame{
public MyFrame(){
TextField textField = new TextField();
add(textField);
//监听这个文本框输入的文字
//按下回车键,就会触发这个输入框的事件,在下边的重写方法中重写的语句为 获得输入框的文本并打印
textField.addActionListener(new My());
//设置替换编码
textField.setEchoChar('*');
setVisible(true);
pack();
}
}
class My implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
TextField text = (TextField) e.getSource();//获得一些资源,返回的一个对象
System.out.println(text.getText());//获得输入框的文本
//每次都设置为空 即每次文本框输入完以后,都会全部删除清零
text.setText("");
}
}
- 简易计算器,组合+内部类回顾复习
oop原则:组合 大于继承
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.TextEvent;
public class TestCALC {
public static void main(String[] args) {
new Calculator();
}
}
//计算器类
class Calculator extends Frame {
public Calculator() {
//3个文本框
TextField textField = new TextField(10);//字符数
TextField textField1 = new TextField(10);//字符数
TextField textField2 = new TextField(20);//字符数
//1 个按钮
Button button = new Button("=");
button.addActionListener(new MyAL(textField,textField1,textField2));
//1个标签
Label label = new Label("+");
setLayout(new FlowLayout());
add(textField);
add(label);
add(textField1);
add(button);
add(textField2);
pack();
setVisible(true);
}
}
//监听器类
class MyAL implements ActionListener {
//获取三个变量
private TextField textField,textField1,textField2;
public MyAL(TextField textField, TextField textField1, TextField textField2){
this.textField = textField;
this.textField1 = textField1;
this.textField2 = textField2;
}
@Override
public void actionPerformed(ActionEvent e) {
//1.获得加数和被加数
int n = Integer.parseInt(textField.getText());
int n1 = Integer.parseInt(textField1.getText());
//2.将这个值加法运算后,放到第三个框
textField2.setText(""+(n+n1));
//3.清除前两个框
textField.setText("");
textField1.setText("");
}
}
改进版本
- 完全改造成面向对象
package com.shuai.lesson2;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.TextEvent;
public class TestCALC {
public static void main(String[] args) {
new Calculator().loadFrame();
}
}
//计算器类
//方法
class Calculator extends Frame {
//属性
TextField textField;
TextField textField1;
TextField textField2;
//方法
public void loadFrame() {
//3个文本框
textField = new TextField(10);//字符数
textField1 = new TextField(10);//字符数
textField2 = new TextField(20);//字符数
//1 个按钮
Button button = new Button("=");
button.addActionListener(new MyAL(this));
//1个标签
Label label = new Label("+");
setLayout(new FlowLayout());
add(textField);
add(label);
add(textField1);
add(button);
add(textField2);
pack();
setVisible(true);
}
}
//监听器类
class MyAL implements ActionListener {
/* //获取三个变量
private TextField textField,textField1,textField2;
public MyAL(TextField textField, TextField textField1, TextField textField2){
this.textField = textField;
this.textField1 = textField1;
this.textField2 = textField2;
}
@Override
public void actionPerformed(ActionEvent e) {
//1.获得加数和被加数
int n = Integer.parseInt(textField.getText());
int n1 = Integer.parseInt(textField1.getText());
//2.将这个值加法运算后,放到第三个框
textField2.setText(""+(n+n1));
//3.清除前两个框
textField.setText("");
textField1.setText("");
}
}
*/
//改进算法
//获取计算器这个对象,在一个类中组合另外一个类
Calculator calculator = null;
public MyAL(Calculator calculator) {
this.calculator = calculator;
}
@Override
public void actionPerformed(ActionEvent e) {
//1.获得加数和被加数
//2.将这个值加法运算后,放到第三个框
//3.清除前两个框
int text = Integer.parseInt(calculator.textField.getText());
int text1= Integer.parseInt(calculator.textField1.getText());
calculator.textField2.setText(""+(text1+text));
calculator.textField.setText("");
calculator.textField.setText("");
}
}
还得把如下所示的代码组合一下,传递监听器麻烦
Calculator calculator = null;
public MyAL(Calculator calculator) {
this.calculator = calculator;
}
内部类
- 最大的好处,就是可以畅通无阻的访问外部的属性和方法
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.TextEvent;
public class TestCALC {
public static void main(String[] args) {
new Calculator().loadFrame();
}
}
//计算器类
//方法
class Calculator extends Frame {
//属性
TextField textField;
TextField textField1;
TextField textField2;
//方法
public void loadFrame() {
//3个文本框
textField = new TextField(10);//字符数
textField1 = new TextField(10);//字符数
textField2 = new TextField(20);//字符数
//1 个按钮
Button button = new Button("=");
button.addActionListener(new MyAL());
//1个标签
Label label = new Label("+");
setLayout(new FlowLayout());
add(textField);
add(label);
add(textField1);
add(button);
add(textField2);
pack();
setVisible(true);
}
private class MyAL implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
//1.获得加数和被加数
//2.将这个值加法运算后,放到第三个框
//3.清除前两个框
int text = Integer.parseInt(textField.getText());
int text1 = Integer.parseInt(textField1.getText());
textField2.setText("" + (text1 + text));
textField.setText("");
textField.setText("");
}
}
画笔
import java.awt.*;
public class TestPaint {
public static void main(String[] args) {
new MYpaint().loadFrame1();
}
}
class MYpaint extends Frame{
public void loadFrame1(){
setBounds(200,200,600,400);
setVisible(true);
}
@Override
public void paint(Graphics g) {
//super.paint(g);有些类的父类有一些初始化操作,不能随便干掉
//画笔,需要颜色,画笔可以画画
g.setColor(Color.red);
g.drawOval(100,100,100,100);
g.fillOval(200,200,100,100);//实心的⚪
g.setColor(Color.green);
g.fillRect(300,300,40,20);
g.drawRect(300,350,40,20);
//养成习惯 画笔画完,将他还原到最初的颜色
g.setColor(Color.BLACK);
}
}
鼠标监听
目的:想要实现鼠标画画!
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Iterator;
//鼠标监听事件
public class TestMouseListener {
public static void main(String[] args) {
new MyFrame("画图");
}
}
//自己的类
class MyFrame extends Frame {
//画画需要画笔,需要监听鼠标当前的位置,需要集合来存储这个点
ArrayList points;
public MyFrame(String title) {
super(title);
setBounds(100, 100, 500, 400);
//存鼠标的点
points = new ArrayList<>();
//鼠标监听器,针对这个窗口
setVisible(true);
this.addMouseListener(new MyML());
}
@Override
public void paint(Graphics g) {
//画画,监听鼠标的事件
Iterator iterator = points.iterator();
while (iterator.hasNext()){
Point point = (Point) iterator.next();
g.setColor(Color.BLUE);
g.fillOval(point.x,point.y,10,10);
}
}
//添加一个点到界面上
public void addPaint(Point point){
points.add(point);
}
//适配器模式
private class MyML extends MouseAdapter {
//鼠标 按下,弹起,按住不放
@Override
public void mouseClicked(MouseEvent e) {
MyFrame myframe = (MyFrame) e.getSource();
//这里我们点击的时候,就会在界面产生一个点
myframe.addPaint(new Point(e.getX(),e.getY()));
//每次点击鼠标都需要重新画一遍
myframe.repaint();//刷新
}
}
}
窗口监听
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class TestWindow {
public static void main(String[] args) {
new WindowF();
}
}
class WindowF extends Frame {
public WindowF() {
setBackground(Color.BLUE);
setBounds(100, 100, 200, 200);
setVisible(true);
this.addWindowListener(
//匿名内部类
new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.out.println("windowsClosing");
System.exit(0);
}
@Override
public void windowActivated(WindowEvent e) {
WindowF source = (WindowF) e.getSource();
source.setTitle("已激活");
System.out.println("windowActivated");
}
});
}
/* @Override
public void windowClosing(WindowEvent e) {
setVisible(false);// 隐藏窗口
System.exit(0);//正常退出 1是非正常退出
};*/
}
键盘监听
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.sql.SQLOutput;
//键
public class TestKeyListener {
public static void main(String[] args) {
new KeyF();
}
}
class KeyF extends Frame{
public KeyF(){
setBounds(0,0,300,400);
setVisible(true);
this.addKeyListener(new KeyAdapter() {
//键盘按下
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();//不需要去记录这个数值,直接使用静态属性VK_xxx
System.out.println(keyCode);
if (keyCode == KeyEvent.VK_UP){
System.out.println("你按了上键盘");
//根据不同的操作,进行不同的结果
}
}
});
}
}
Swing
- 做界面
###窗口、面板JFrame
import javax.swing.*;
import java.awt.*;
public class JFrameDemo {
//init();初始化
public void init(){
//顶级窗口
JFrame jf = new JFrame("这是一个JFrame窗口");
jf.setBounds(100,100,400,300);
//设置文字Label->JLabel
jf.setBackground(Color.BLUE);
JLabel jl = new JLabel("JJJJJ");
jf.add(jl);
//让文本标签居中
jl.setHorizontalAlignment(SwingConstants.CENTER);
//容器实例化
jf.getContentPane().setBackground(Color.red);
jf.setVisible(true);
//关闭事件
jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
//建立一个窗口
new JFrameDemo().init();
}
}
弹窗
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class DialogDemo extends JFrame {
public DialogDemo() {
this.setVisible(true);
this.setBounds(100, 100, 400, 400);
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
//Jframe 放东西,容器
Container contentPane = this.getContentPane();
//绝对布局
contentPane.setLayout(null);
//设置背景
contentPane.setBackground(Color.BLUE);
//按钮
JButton jButton = new JButton("点击弹出一个对话框");
jButton.setBounds(30, 30, 200, 50);
//点击按钮弹出弹框
jButton.addActionListener(new ActionListener() {//监听器
@Override
public void actionPerformed(ActionEvent e) {
//弹窗
new MyDialog();
}
});
contentPane.add(jButton);
}
public static void main(String[] args) {
new DialogDemo();
}
}
//弹窗的窗口
class MyDialog extends JDialog {
public MyDialog() {
this.setVisible(true);
this.setBounds(100, 100, 500, 500);
// this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
//JDialog退出只能是D0_ONTHING,HIDE,DISPOSE这三个中的一种
//应该是默认就有关闭事件
this.setTitle("这是一个弹窗");
Container contentPane = this.getContentPane();
contentPane.setLayout(null);
contentPane.setBackground(Color.ORANGE);
JLabel jjj = new JLabel("学习学习");
contentPane.add(jjj);
jjj.setBounds(20,20,50,50);
}
}
标签
- label
new JLabel("xxx");
- 图标Icon
import javax.swing.*;
import java.awt.*;
//图标,需要实现类,Frame继承
public class IconDemo extends JFrame implements Icon {
private int width;
private int hight;
public IconDemo(){};//无参构造
//有参构造
public IconDemo(int width,int hight){
this.width = width;
this.hight = hight;
};
public void init(){
IconDemo iconDemo = new IconDemo(15, 15);
//图标可以放在标签,也可以放在按钮上!
JLabel jLabel = new JLabel("标签",iconDemo,SwingConstants.CENTER);
Container contentPane = getContentPane();
contentPane.add(jLabel);
this.setVisible(true);
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {
g.fillOval(x,y,width,hight);
}
@Override
public int getIconWidth() {
return this.width;
}
@Override
public int getIconHeight() {
return this.hight;
}
public static void main(String[] args) {
new IconDemo().init();
}
}
- 图片
import javax.swing.*;
import java.awt.*;
import java.net.URL;
public class ImageIconDemo extends JFrame {
public ImageIconDemo(){
JLabel jLabel = new JLabel("图片");
URL resource = ImageIconDemo.class.getResource("4.png");
ImageIcon imageIcon = new ImageIcon(resource);
jLabel.setIcon(imageIcon);
jLabel.setHorizontalAlignment(SwingConstants.CENTER);
Container contentPane = getContentPane();
contentPane.add(jLabel);
this.setVisible(true);
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setBounds(100,100,800,800);
}
public static void main(String[] args) {
new ImageIconDemo();
}
}
###面板
- JPanel
import javax.swing.*;
import java.awt.*;
public class JPanelDemo extends JFrame {
public JPanelDemo(){
Container contentPane = this.getContentPane();
contentPane.setLayout(new GridLayout(2,1,10,10));//后边两个是间距
JPanel jPanel = new JPanel(new GridLayout(1, 3));
JPanel jPane2 = new JPanel(new GridLayout(1, 2));
JPanel jPane3 = new JPanel(new GridLayout(1, 1));
jPanel.add(new JButton("aaa"));
jPanel.add(new JButton("bbb"));
jPanel.add(new JButton("ccc"));
jPane2.add(new JButton("111"));
jPane2.add(new JButton("222"));
jPane3.add(new JButton("---"));
setBounds(100,100,500,400);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
contentPane.add(jPanel);
contentPane.add(jPane2);
contentPane.add(jPane3);
setVisible(true);
contentPane.setBackground(Color.YELLOW);
}
public static void main(String[] args) {
new JPanelDemo();
}
}
- JscrollPanel
import javax.swing.*;
import java.awt.*;
public class JScrollPanelDemo extends JFrame {
public JScrollPanelDemo(){
Container contentPane = this.getContentPane();
//文本域
JTextArea jTextArea = new JTextArea(20, 50);
jTextArea.setText("学习学习");
//面板 并添加到contentpane
contentPane.add(new JScrollPane(jTextArea));
this.setVisible(true);
this.setBounds(100,100,400,300);
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
contentPane.setBackground(Color.BLUE);
}
public static void main(String[] args) {
new JScrollPanelDemo();
}
}
按钮
- 图片按钮
import javax.swing.*;
import java.awt.*;
import java.net.URL;
public class JButtonDemo01 extends JFrame {
public JButtonDemo01(){
Container contentPane = this.getContentPane();
//图片变为图标
URL resource = JButtonDemo01.class.getResource("4.png");
Icon icon = new ImageIcon(resource);
JButton jButton = new JButton();
jButton.setIcon(icon);
//悬浮框
jButton.setToolTipText("这是一个图片按钮");
contentPane.add(jButton);
this.setVisible(true);
this.setBounds(100,100,400,300);
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
new JButtonDemo01();
}
}
- 单选按钮
import javax.swing.*;
import java.awt.*;
import java.net.URL;
public class JButtonDemo02 extends JFrame {
public JButtonDemo02(){
Container contentPane = this.getContentPane();
//图片变为图标
URL resource = JButtonDemo01.class.getResource("4.png");
Icon icon = new ImageIcon(resource);
//单选框
JRadioButton jrb01 = new JRadioButton("jrb01");
JRadioButton jrb02 = new JRadioButton("jrb02");
JRadioButton jrb03 = new JRadioButton("jrb03");
//由于单选框只能选择一个,分组
ButtonGroup buttonGroup = new ButtonGroup();
buttonGroup.add(jrb01);
buttonGroup.add(jrb02);
buttonGroup.add(jrb03);
contentPane.add(jrb01,BorderLayout.CENTER);
contentPane.add(jrb02,BorderLayout.NORTH);
contentPane.add(jrb03,BorderLayout.SOUTH);
this.setVisible(true);
this.setBounds(100,100,400,300);
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
new JButtonDemo02();
}
}
- 复选按钮
import javax.swing.*;
import java.awt.*;
import java.net.URL;
public class JButtonDemo03 extends JFrame {
public JButtonDemo03(){
Container contentPane = this.getContentPane();
//图片变为图标
URL resource = JButtonDemo01.class.getResource("4.png");
Icon icon = new ImageIcon(resource);
//多选框
JCheckBox jcb1 = new JCheckBox("jcb1");
JCheckBox jcb2 = new JCheckBox("jcb2");
JCheckBox jcb3 = new JCheckBox("jcb3");
//流式布局
contentPane.setLayout(new FlowLayout(FlowLayout.LEFT));
contentPane.add(jcb1);
contentPane.add(jcb2);
contentPane.add(jcb3);
//东西南北中布局
/*
contentPane.add(jcb1,BorderLayout.NORTH);
contentPane.add(jcb2,BorderLayout.CENTER);
contentPane.add(jcb3,BorderLayout.SOUTH);
*/
this.setVisible(true);
this.setBounds(100,100,400,300);
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
new JButtonDemo03();
}
}
列表
- 下拉框
import javax.swing.*;
import java.awt.*;
public class TestComboboxDemo01 extends JFrame {
public TestComboboxDemo01(){
Container container = this.getContentPane();
JComboBox status = new JComboBox();
status.addItem("未上映");
status.addItem("正在热映");
status.addItem("已下架");
container.add(status);
this.setVisible(true);
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
this.setBounds(100,100,500,400);
}
public static void main(String[] args) {
new TestComboboxDemo01();
}
}
- 列表框
import javax.swing.*;
import java.awt.*;
import java.util.Vector;
public class TestComboboxDemo02 extends JFrame {
public TestComboboxDemo02(){
Container container = this.getContentPane();
//生成列表的内容
// String[] contents = {"1","2","3"};
//列表中需要的内容
Vector contents = new Vector();
JList jList = new JList(contents);
JList jList1 = new JList(contents);
contents.add("2222");
contents.add("333");
container.add(jList);
this.setVisible(true);
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
this.setBounds(100,100,500,400);
}
public static void main(String[] args) {
new TestComboboxDemo02();
}
}
文本框
import javax.swing.*;
import java.awt.*;
public class TestTextDemo01 extends JFrame {
public TestTextDemo01(){
Container container = this.getContentPane();
//不布局只会出现WORLD,且位置不对
this.setLayout(new FlowLayout(FlowLayout.RIGHT));
JTextField jTextField1 = new JTextField("HELLO");
JTextField jTextField2 = new JTextField("WORLD",20);
container.add(jTextField1);
container.add(jTextField2);
this.setVisible(true);
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
this.setBounds(100,100,400,300);
}
public static void main(String[] args) {
new TestTextDemo01();
}
}
###密码框
import javax.swing.*;
import java.awt.*;
public class TestTextDemo02 extends JFrame {
public TestTextDemo02(){
Container container = this.getContentPane();
JPasswordField jPasswordField = new JPasswordField();//---
jPasswordField.setEchoChar('-');
container.add(jPasswordField);
this.setVisible(true);
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
this.setBounds(100,100,400,300);
}
public static void main(String[] args) {
new TestTextDemo02();
}
}
文本域
//文本域
JTextArea jTextArea = new JTextArea(20, 50);
jTextArea.setText("学习学习");
//面板 并添加到contentpane
contentPane.add(new JScrollPane(jTextArea));
注解和反射
注解
- Annotation是从JDK5.O开始引入的新技术.
- Annotation的作用:
- 不是程序本身,可以对程序作出解释.(这一点和注释(comment)没什么区别)
- 可以被其他程序(比如:编译器等)读取
- Annotation的格式:
注解是以"@注释名"在代码中存在的,还可以添加一些参数值,例
&如:@SuppressWarnings(value=“unchecked”). - Annotation在哪里使用?
可以附加在package,class,method,field等上面,相当于给他们添加了额外的辅助信息,
我们可以通过反射机制编程实现对这些元数据的访问
###内置注解
@Override:定义在java.lang.Override中,此注释只适用于修辞方法,表示一个方法声明打算
重写超类中的另一个方法声明.
@Deprecated:定义在java.ang.Deprecated中,此注释可以用于修辞方法,属性,类,表示不
鼓励程序员使用这样的元素,通常是因为它很危险或者存在更好的选择.
@SuppressWarnings:定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息.
口与前两个注释有所不同,你需要添加一个参数才能正确使用,这些参数都是已经定义好了的,
我们选择性的使用就好了.
- @SuppressWarnings(“all”)
- @SuppressWarnings(“unchecked”)
- @SuppressWarnings(value={“unchecked”,“deprecation”})
// 什么是注解
public class Demo01_Annotation extends Object {
// @Override就是一个注解
@Override
public String toString() {
return super.toString();
}
// @Deprecated不推荐程序员使用,但是可以使用,或者存在更好的更新方式
@Deprecated
public static void test() {
System.out.println("Deprecated");
}
// @SuppressWarnings 镇压警告
@SuppressWarnings("all")
public void test01(){
List<String> list = new ArrayList<String>();
}
public static void main(String[] args) {
test();
}
}
###元注解
- 作用:负责注解其他注解,Java定义了4个标准的meta-annotation类型,他们被用来
提供对其他annotation类型作说明. - 这些类型和它们所支持的类在java.lang.annotation包中可以找到.(@Target,@Retention,
@Documented @Inherited
@Target:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
@Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期
(SOURCE CLASS RUNTIME)
@Document:说明该注解将被包含在javadoc中
@Inherited:说明子类可以继承父类中的该注解
//测试元注解
@MyAnnotation
public class Demo02_MetaAnnotation {
@MyAnnotation
public void test() {
}
}
//定义一个注解
//@Target可以用在什么地方
//ElementType.METHOD方法上有效 ElementType.TYPE类上有效
@Target(value = {ElementType.METHOD, ElementType.TYPE})
//@Retention在什么地方有效
//RUNTIME>CLASS>SOURCES
@Retention(value = RetentionPolicy.RUNTIME)
//@Documented 表示是否将我们的注解生成在Javadoc中
@Documented
//@Inherited 子类可以继承父类的注解
@Inherited
@interface MyAnnotation { }
###自定义注解
自定义注解
使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口
分析:
@interface)用来声明一个注解,格式:public@interface注解名{定义内容}
其中的每一个方法实际上是声明了一个配置参数
方法的名称就是参数的名称.
返回值类型就是参数的类型(返回值只能是基本类型,Class,String,enum).
可以通过default来声明参数的默认值
如果只有一个参数成员,一般参数名为value
注解元素必须要有值,我们定义注解元素时,经常使用空字符串,作为默认值.
//自定义注解
public class Demo03_CustomAnnotation {
//注解可以显示赋值,如果没有默认值,就必须给注解赋值
@MyAnnotation2(name = "张三")
public void test() {
}
}
@Target(value = {ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2 {
//注解的参数:参数类型+参数名()
//String name();
String name() default "";
int age() default 0;
int id() default -1;//-1代表不存在
String[] schools() default {"西部开源","清华大学"};
反射
静态VS动态语言
- 动态语言
是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被
引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代
码可以根据某些条件改变自身结构。- 主要动态语言:Object-C、C#、JavaScript、PHP、Python等。
- 静态语言
与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、C++。
Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,
我们可以利用反射机制获得类似动态语言的特性。Java的动态性让编程的时候更
加灵活!
###反射机制概念
Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借
助于Reflection AP取得任何类的内部信息,并能直接操作任意对象的内部属性及
方法。
Class c=Class.forName("java.lang.String")
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有
一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对
象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,
我们形象的称之为:反射
- 反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
反射机制优缺点
- 优点:可以实现动态创建对象和编译,体现出很大的灵活性
- 缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于直接执行相同的操作。
实现:
//什么叫反射
public class Demo04_Reflection {
public static void main(String[] args) throws ClassNotFoundException {
// 通过反射获取类的class对象
Class<?> c = Class.forName("cn.doris.reflection.User");
System.out.println(c);
Class<?> c1 = Class.forName("cn.doris.reflection.User");
Class<?> c2 = Class.forName("cn.doris.reflection.User");
Class<?> c3 = Class.forName("cn.doris.reflection.User");
Class<?> c4 = Class.forName("cn.doris.reflection.User");
// 一个类在内存中只有一个Class对象
// 一个类被加载后,类的整个结构都会被封装在Class对象中
/**
* public native int hashCode();返回该对象的hash码值
* 注:哈希值是根据哈希算法算出来的一个值,这个值跟地址值有关,但不是实际地址值。
*/
System.out.println(c1.hashCode());
System.out.println(c2.hashCode());
System.out.println(c3.hashCode());
System.out.println(c4.hashCode());
}
}
//实体类
class User {
private String name;
private int id;
private int age;
public User() {
}
public User(String name, int id, int age) {
this.name = name;
this.id = id;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Class类
- Class本身也是一个类
- Class对象只能由系统建立对象
- 一个加载的类在JVM中只会有一个Class示例
- 一个Class对象对应的是一个加载到JVM中的一个.class文件
- 每个类的示例都会记得自己是由哪个Class示例所生成的
- 通过Class可以完整的得到一个类中的所有被加载的结构
- Class类是Relection的根源,针对任何你想动态加载、运行的类,唯有先获得相应Class对象
####Class类常用方法
- Class.forName();动态加载类。
- newInstance() :根据对象的class新建一个对象
- getSuperclass() 获取继承的父类
- getInterfaces() 获取继承的接口
- getDeclaredFields() 获取字段名字
- getDeclaredMethods();获取当前类的所有方法
- getConstructors() :获得所有的构造函数。
- getModifiers() : 反射中获得修饰符
- getPackage() :反射中获得package
- getField(String name):反射中获得域成员。
- getFields() :获得域数组成员。
- isAnnotation() :判断是否为注解类型。
- isPrimitive() :判断是否为基本类型。
- isArray() :判断是否为数组类型。
- isEnum() :判断是否为枚举类型。
- getClassLoader() :获得类的类加载器
- getMethods() 获得公共的方法。
####获取Class类的实例
- 若已知具体的类,通过类的class属性获取:安全可靠,程序性能最高
Class c = Person.class;
- 已知某个类的实例,调用该实例的getClass()方法获取Class对象
Class c = person.getClass();
- 已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFountException
Class c = Class.foName("");
- 内置基本数据类型可以直接用类名.Type
- 还可以利用ClassLoader
//测试class类的创建方式有哪些
public class Demo05_CreateClass {
public static void main(String[] args) throws ClassNotFoundException {
Person person = new Student();
System.out.println("这个人是:"+person.name);
//方式一:通过对象查询
Class c1 = person.getClass();
System.out.println(c1.hashCode());
//方式二:forname获得
Class c2 = Class.forName("cn.doris.reflection.Student");
System.out.println(c2.hashCode());
//方式三:通过类名.class获得
Class c3 = Student.class;
System.out.println(c3.hashCode());
//方式四,基本类型的包装类都有一个Type
Class c4 = Integer.TYPE;
System.out.println(c4);
//获得父类类型
Class c5 = c1.getSuperclass();
System.out.println(c5);
}
}
class Person {
String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name=" + name +
'}';
}
}
class Student extends Person {
public Student() {
this.name = "学生";
}
}
class Teacher extends Person {
public Teacher() {
this.name = "老师";
}
}
- 可以有Class对象的类型
- class:外部类,成员,局部内部类,匿名内部类
- interface:接口
- []:数组
- enum:枚举
- annotation:注解@interface
- primitive type:基本数据类型
- void
//所有类型的Class
public class Demo06_AllTypeClass {
public static void main(String[] args) {
Class c1 = Object.class; //类
Class c2 = Comparable.class; //接口
Class c3 = String[].class; //一维数组
Class c4 = int[][].class; //二维数组
Class c5 = Override.class; //注解
Class c6 = ElementType.class; //枚举
Class c7 = Integer.class; //基本数据类型
Class c8 = void.class; //void
Class c9 = Class.class; //class
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);
//只要元素类型与维度一样,就是同一个Class
int[] a = new int[10];
int[] b = new int[100];
System.out.println(a.getClass().hashCode());
System.out.println(b.getClass().hashCode());
}
}
Java内存分析
###类的加载过程
- 什么时候会发生类的初始化
类的主动引用一定会发生类的初始化
- 当JVM虚拟机启动,先初始化main方法所在的类
- new 一个类的对象
- 调用类的静态成员(final常量除外)和静态方法
- 使用java.lang.reflect包的方法对类进行反射调用
- 当初始化一个类,如果其父类没有被初始化,则会先初始化他的父类
###类加载器的作业
将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
.TYPE;
System.out.println(c4);
//获得父类类型
Class c5 = c1.getSuperclass();
System.out.println(c5);
}
}
class Person {
String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return “Person{” +
“name=” + name +
‘}’;
}
}
class Student extends Person {
public Student() {
this.name = “学生”;
}
}
class Teacher extends Person {
public Teacher() {
this.name = “老师”;
}
}
* 可以有Class对象的类型
* class:外部类,成员,局部内部类,匿名内部类
* interface:接口
* []:数组
* enum:枚举
* annotation:注解@interface
* primitive type:基本数据类型
* void
~~~java
//所有类型的Class
public class Demo06_AllTypeClass {
public static void main(String[] args) {
Class c1 = Object.class; //类
Class c2 = Comparable.class; //接口
Class c3 = String[].class; //一维数组
Class c4 = int[][].class; //二维数组
Class c5 = Override.class; //注解
Class c6 = ElementType.class; //枚举
Class c7 = Integer.class; //基本数据类型
Class c8 = void.class; //void
Class c9 = Class.class; //class
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);
//只要元素类型与维度一样,就是同一个Class
int[] a = new int[10];
int[] b = new int[100];
System.out.println(a.getClass().hashCode());
System.out.println(b.getClass().hashCode());
}
}
Java内存分析
###类的加载过程
- 什么时候会发生类的初始化
类的主动引用一定会发生类的初始化
- 当JVM虚拟机启动,先初始化main方法所在的类
- new 一个类的对象
- 调用类的静态成员(final常量除外)和静态方法
- 使用java.lang.reflect包的方法对类进行反射调用
- 当初始化一个类,如果其父类没有被初始化,则会先初始化他的父类
###类加载器的作业
将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。