⭐java泛型

  • Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,代码更加简洁、健壮。

集合中使用泛型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Test
void t4() {
ArrayList<Integer> list = new ArrayList<>();//类型推断
list.add(78);
list.add(88);
list.add(77);
list.add(66);
// 遍历方式一:
for (Integer i : list) {
// 不需要强转
System.out.println(i);
}
//遍历方式二:
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("Tom1", 34);
map.put("Tom2", 44);
map.put("Tom3", 33);
map.put("Tom4", 32);
//添加失败
//map.put(33, "Tom");
Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
Iterator<Map.Entry<String, Integer>> iterator = entrySet.iterator();
while (iterator.hasNext()) {
Map.Entry<String, Integer> entry = iterator.next();
System.out.println(entry.getKey() + "--->" + entry.getValue());
}
}

自定义泛型

泛型类

泛型的声明

interface List 和 class GenTest<K,V> 其中,T,K,V不代表值,而是表示类型。这里使用任意字母都可以。常用T表示,是Type的缩写。

T只能是类,不能用基本数据类型填充。但可以使用包装类填充

把一个集合中的内容限制为一个特定的数据类型,这就是generics背后的核心思想

  1. 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:<E1,E2,E3>

  2. 泛型类的构造器如下:public GenericClass(){}。

    • 而下面是错误的:public GenericClass(){}
  3. 实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。

  4. 泛型不同的引用不能相互赋值。

  5. 尽管在编译时ArrayList和ArrayList是两种类型,但是,在运行时只有一个ArrayList被加载到JVM中。

  6. 泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。经验:==泛型要使用一路都用。要不用,一路都不要用。==

  7. 如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。

  8. jdk1.7,泛型的简化操作:ArrayList flist = new ArrayList<>();

  9. ==泛型的指定中不能使用基本数据类型==,可以使用包装类替换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class GenericTest {
public static void main(String[] args) {
// 1、使用时:类似于Object,不等同于Object
ArrayList list = new ArrayList();
// list.add(new Date());//有风险
list.add("hello");
test(list);// 泛型擦除,编译不会类型检查
// ArrayList<Object> list2 = new ArrayList<Object>();
// test(list2);//一旦指定Object,编译会类型检查,必须按照Object处理
}
public static void test(ArrayList<String> list) {
String str = "";
for (String s : list) {
str += s + ","; }
System.out.println("元素:" + str);
}
}
  1. 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型。

  2. ==异常类不能是泛型的==

  3. 不能使用new E[]。但是可以:E[] elements = (E[])new Object[capacity];参考:ArrayList源码中声明:Object[] elementData,而非泛型参数类型数组。

  4. 父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:

    • 子类不保留父类的泛型:按需实现

      1.没有类型 擦除

      2.具体类型

    • 子类保留父类的泛型:泛型子类

      1.全部保留

      2.部分保留

结论:子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自己的泛型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Father<T1, T2> {
}
// 子类不保留父类的泛型
// 1)没有类型 擦除
class Son1 extends Father {// 等价于class Son extends Father<Object,Object>{
}
// 2)具体类型
class Son2 extends Father<Integer, String> {
}
// 子类保留父类的泛型
// 1)全部保留
class Son3<T1, T2> extends Father<T1, T2> {
}
// 2)部分保留
class Son4<T2> extends Father<Integer, T2> {
}
12.3 自定义泛型结构:泛型类、泛型接口
class Father<T1, T2> {
}
// 子类不保留父类的泛型
// 1)没有类型 擦除
class Son<A, B> extends Father{//等价于class Son extends Father<Object,Object>{
}
// 2)具体类型
class Son2<A, B> extends Father<Integer, String> {
}
// 子类保留父类的泛型
// 1)全部保留
class Son3<T1, T2, A, B> extends Father<T1, T2> {
}
// 2)部分保留
class Son4<T2, A, B> extends Father<Integer, T2> {
}

泛型方法

方法,也可以被泛型化,不管此时定义在其中的类是不是泛型类。在泛型

方法中可以定义泛型参数,此时,参数的类型就是传入数据的类型。

泛型方法的格式:

[访问权限]<泛型> 返回类型 方法名([泛型标识 参数名称])

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public <E> List<E> copyFromArrayToList(E[] arr) {
//Order.java 的非静态方法
ArrayList<E> list = new ArrayList<E>();
for (E temp : arr)
list.add(temp);
return list;
//return new ArrayList<E>(Arrays.asList(arr)); 一行代码替代
}
@Test
void t2() {
Integer[] arr = new Integer[]{12, 54, 76, 8, 978};
Order<String> order = new Order<>();
//泛型方法在调用时,指明泛型参数的类型
List<Integer> list = order.copyFromArrayToList(arr);
}


泛型在继承上的体现

如果B是A的一个子类型(子类或者子接口),而G是具有泛型声明的类或接口,G并不是G的子类型

注意:A B可以转换

1
2
3
List<String> list1=null;
ArrayList<String> list2=null;
list1=list2;
1
2
3
4
5
6
7
8
9
@Test
void t1() {
Object obj = null;
String str = null;
obj = str; //具备子父类关系 ,父类的 引用可以指向子类的对象
List<Object> list1 = null;
List<String> list2 = null;
//keypoint list1 = (List<Object>) list2; //此时的list1 list2 不具备子父类关系 ,编译不通过
}

通配符的使用

  • 通配符: ?
1
2
3
4
5
6
7
8
9
@Test
void t2() {
List<String> list1 = null;
ArrayList<String> list2 = null;
list1 = list2;
List<?> list = null;
list = list1;
list = list2;
}

类A是类B的父类,G与G没有关系,二者共同的父类是G<?>;

1.使用通配符后的读取和写入要求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Test
void t2() {
List<String> list1 = null;
ArrayList<String> list2 = null;
list1 = list2;
List<?> list = null;
list = list1;
list = list2;
List<String> list3 = new ArrayList<>();
list3.add("AAA");
list3.add("BBB");
list3.add("CCC");
//keypoint
// 添加:对于List<?>不能像其内部添加数据
// (防止新添加的数据与原有的类型冲突),除了可以添加null(null不是任何类型的对象)

list = list3;
System.out.println(list.get(1));
//keypoint
// 可以读取数据,get()的返回值是 Object类型
print(list);
}

无法向List<?>中添加元素,可以get(int idx),返回值是Object类型

2.有限制条件的通配符的使用

1
2
3
4
5
6
7
8
9
10
11
12
@Test
void t3() {
List<? extends Person> list1 = null;
// <= ,可以作为
List<? super Person> list2 = null; // >=
List<Person> list3 = null;
List<Object> list4 = null;
list1 = list3;
// list1 = list4; error
list2 = list3;
list2 = list4;
}
  • 注意加了限制条件的 List<?>仍然无法add()
1
2
3
4
5
6
7
public static void main(String[] args) {
List<? extends Object> list = null;
// list.add("423");//error
List<String> list1 = null;
list1.add("4234");
list = list1;
}

🌟java IO流

File类的使用

  • File 能新建、删除、重命名文件和目录,但 File 不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流。
  • java.io package下

i: input

o:output

1.File类的实例化

==注意==:如果File 对象初始化的文件不存在,那么此时的File对象仅仅只是内存中的一个对象,不会创建文件。

常用构造器

public File(String pathname)

  • 以pathname为路径创建File对象,可以是绝对路径或者相对路径,如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储。

  • 绝对路径:是一个固定的路径,从盘符开始

  • 相对路径:是相对于某个位置开始

public File(String parent,String child)

  • 以parent为父路径,child为子路径创建File对象。

public File(File parent,String child)

  • 根据一个父File对象和子文件路径创建File对象

路径分隔符

1.路径中的每级目录之间用一个路径分隔符隔开。

2.路径分隔符和系统有关:

  • windows和DOS系统默认使用“\”来表示

  • UNIX和URL使用“/”来表示

3.java程序支持跨平台运行,因此路径分隔符要慎用。

  • 为了解决这个隐患,File类提供了一个常量:

    public static final String separator

    根据操作系统,动态的提供分隔符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
void t1() {
//构造器1
File file1 = new File("C:/Users/lenovo/Desktop/javaIO.txt");
File file2 = new File("javaIO.txt");
System.out.println(file1);
System.out.println(file2.getClass());
//C:\Users\lenovo\Desktop,由于转义字符 ,所以用 / 或者\\
//构造器2
File file3 = new File("C:/Users/lenovo/Desktop", "Cpp");
System.out.println(file3);
//构造器3
File file4 = new File(file3, "javaIO file4.txt");
System.out.println(file4);
}

2.常用方法

1️⃣File类的获取功能

  • public String getAbsolutePath():获取绝对路径
  • public String getPath() :获取路径
  • public String getName() :获取名称
  • public String getParent():获取上层文件目录路径。若无,返回null
  • public long length() :获取文件长度(即:字节数)。不能获取目录的长度。
  • public long lastModified() :获取最后一次的修改时间,毫秒值
  • public String[] list() :获取指定目录下的所有文件或者文件目录的名称数组
  • public File[] listFiles() :获取指定目录下的所有文件或者文件目录的File数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Test
void t2()
{
SimpleDateFormat sdf = new SimpleDateFormat();
File file1 = new File("helloJavaIO.txt");
File file2 = new File("C:/Users/lenovo/Desktop/helloJavaIO.txt");
System.out.println("绝对路径 " + file1.getAbsolutePath());
System.out.println("相对路径 " + file1.getPath());
System.out.println("文件名 " + file1.getName());
System.out.println("获取上级目录 " + file1.getParent());
System.out.println("长度" + file1.length());
System.out.println("最后一次的修改时间(已经使用SimpleDateFormat格式化) :" + sdf.format(file1.lastModified()));
System.out.println();
System.out.println(file2.getAbsolutePath());
System.out.println(file2.getPath());
System.out.println(file2.getName());
System.out.println(file2.getParent());
System.out.println(file2.length());
System.out.println(file2.lastModified());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
void t3() {
File file = new File("C:/Users/lenovo/Desktop/java/SUMMER_JAVA");
String[] list = file.list();
for (String s : list) {
System.out.println(s);
}
File[] files = file.listFiles();
System.out.println();
try {
for (File f : files) {
System.out.println(f);
}
} catch (Exception e) {
e.printStackTrace();
}
}

2️⃣File类的重命名功能

  • public boolean renameTo(File dest):把文件重命名为指定的文件路径13.1 File 类的使用:常用方法
1
2
3
4
5
6
7
8
9
10
    @Test
void t4() {

File file1 = new File("helloJavaIO.txt");
File file2 = new File("C:/Users/lenovo/Desktop/IOTest.txt");
boolean renameTo = file1.renameTo(file2);
/* Keypoint
要想保证返回True,需要保证file1在鹰派债市存在的,且file2不能再硬盘中存在*/
System.out.println(renameTo);
}

3️⃣File类的判断功能

  • public boolean isDirectory():判断是否是文件目录
  • public boolean isFile() :判断是否是文件
  • public boolean exists() :判断是否存在
  • public boolean canRead() :判断是否可读
  • public boolean canWrite() :判断是否可写
  • public boolean isHidden() :判断是否隐藏
1
2
3
4
5
6
7
8
void t5() {
File file = new File("helloJavaIO.txt");
System.out.println(file.isFile());
System.out.println(file.isDirectory());
System.out.println(file.exists());
System.out.println(file.canRead());
System.out.println(file.isHidden());
}

4️⃣File类的创建功能

  • public boolean createNewFile() :创建文件。若文件存在,则不创建,返回false

  • public boolean mkdir() :创建文件目录。如果此文件目录存在,就不创建了。如果此文件目录的上层目录不存在,也不创建。

  • public boolean mkdirs() :创建文件目录。如果上层文件目录不存在,一并创建

注意事项:如果你创建文件或者文件目录没有写盘符路径,那么,默认在项目路径下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Test
void t6() {
File file = new File("C:/Users/lenovo/Desktop/Test.doc");
if (!file.exists()) {
try {
file.createNewFile(); //文件创建
System.out.println("创建成功! ");
} catch (IOException e) {
e.printStackTrace();
}
} else {
if (file.delete())
System.out.println("delete successfully!");
}
//文件目录的创建
File file1 = new File("C:/Users/lenovo/Desktop/Test");
if (!file1.exists())
if (file1.mkdir())
System.out.println("mkdir()创建成功!");
File file2 = new File("C:/Users/lenovo/Desktop/TEstIO/hello");
if (!file2.exists())
if (file2.mkdirs())
System.out.println("mkdirs()创建成功!");
if (file.exists())
file.delete();
}

5️⃣File类的删除功能

  • public boolean delete():删除文件或者文件夹

  • ==删除注意事项==:

    1. Java中的删除不走回收站
    2. 要删除一个文件目录,请注意该文件目录内不能包含文件或者件目录

练习:判断指定目录下是否有后缀名为.jpg的文件,如果有,就输出该文件名称

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public static void main(String[] args) {
t(new File("E:\\vscode_background"));
}

static void t(File file) {
//keypoint
// 判断指定目录下是否有后缀名为.jpg的文件,如果有,就输出该文件名称
File[] files = file.listFiles();
for (File file1 : files) {
String str = new String(file1.getName());
str = new String(str.substring(str.length() - 3, str.length()));
// System.out.println(file1.getName());
if (str.equals("jpg")) {
System.out.println(file1.getName());
}
}
}

static void f(File file) {
String[] name = file.list();
for (String s : name) {
if (s.substring(s.length() - 3).equals("jpg")) {
System.out.println(s);
}
}
}