0
点赞
收藏
分享

微信扫一扫

java查漏补缺(1)

yellowone 2022-05-04 阅读 117
java

循环

foreach

增强型for循环只能用来取值,却不能用来修改数组里的值:

//User类,下面要用
public class User {
    public int id;
    public String name;

    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}
	public static void foreachTest() {
        int[] ints = {1, 2, 3, 4, 5};
        for (int i : ints) {
            //无效,值传递,改变这个值并不能改变原有数组的值
            i = 1;
            System.out.println(i);
        }
        System.out.println(Arrays.toString(ints));//[1, 2, 3, 4, 5]
    }

    public static void foreachTest2() {

        ArrayList<User> users = new ArrayList<>();
        users.add(new User(1, "name1"));
        users.add(new User(2, "name2"));
        for (User i : users) {
            //无效,一开始i存的是users的地址,这样只是改变了i的地址,也和users没关系
            i = new User(3, "name3");
        }
        for (User i : users) {
            System.out.println(i);
        }
    }

    public static void foreachTest3() {

        ArrayList<User> users = new ArrayList<>();
        users.add(new User(1, "name1"));
        users.add(new User(2, "name2"));
        for (User i : users) {
            //有效,通过地址直接改变了users集合内对象的属性值。
            i.name = "updateName";
        }
        for (User i : users) {
            System.out.println(i);
        }
    }

foreach还可以遍历枚举:

public static void foreachTest4() {
		//Season枚举类见下段
        for (Season season : Season.values()) {
            System.out.println(season);
        }
    }

switch

switch是离散式判断,可以跟枚举类型:

public enum Season {
    SPRING,
    SUMMER,
    AUTUMN,
    WINTER
}
	public static void enumTest(){
        Season season = Season.SPRING;
        switch (season){
            case SPRING:
                System.out.println("spring");
                break;
            case SUMMER:
                System.out.println("summer");
                break;
            case AUTUMN:
                System.out.println("autumn");
                break;
            case WINTER:
                System.out.println("winter");
                break;
        }
    }

继承与接口

多态

分为运算符多态和类多态。
运算符多态:比如"+"运算符可以加数字也可以拼接字符串。
类多态:同一个类的不同对象执行同一方法,结果不同。
类多态条件:
1.两个类继承同一个父类(或同一个接口),然后重写父类的方法(或实现接口方法)。
2.两个类都调用父类(或接口)的方法。
这样可以保证编写代码的时候方法调用的统一,不用根据具体类调一个具体方法。

隐藏

隐藏,就是子类继承父类后,重写了父类的静态方法。
因为静态方法和静态变量不能改变/重写,所以这里的重写实际上是替换、隐藏了父类的静态方法。

//父类
public class Hero {
    public String name;
    protected float hp;
    
    //类方法,静态方法
    //通过类就可以直接调用
    public static void battleWin(){
        System.out.println("hero battle win");
    }
}
//子类
public class ADHero extends Hero{
    //隐藏父类的battleWin方法
    public static void battleWin(){
        System.out.println("ad hero battle win");
    }
}
	public static void main(String[] args) {
        Hero h =new ADHero();
        ADHero adHero = new ADHero();
        h.battleWin();//hero battle win(执行了父类方法)
        adHero.battleWin();//ad hero battle win(执行子类自己的方法)
    }

super

实例化一个ADHero(), 其构造方法会被调用,其父类的构造方法也会被调用
父类构造方法先调用,子类构造方法会默认调用父类的无参的构造方法
当父类只有一个有参构造方法,则子类的构造器必须通过super显式调用父类的有参构造方法。

Object类

所有类继承自Object类。
Object类中自带的方法:

  1. toString():返回当前对象的字符串表达
  2. finalize():
    当一个对象没有任何引用指向的时候,它就满足垃圾回收的条件。
    当它被垃圾回收的时候,它的finalize() 方法就会被调用。
    finalize() 不是开发人员主动调用的方法,而是由虚拟机JVM调用的。
  3. equals():判断两个对象的内容是否相同,不是判断地址,判断地址请用“==”
  4. hashCode():返回一个对象的哈希值
  5. 线程同步相关方法:wait()、notify()、notifyAll(),讲线程再说
  6. getClass():返回一个对象的类对象,反射用的多

final

  1. 修饰的类不能被继承
  2. 修饰的方法不能被重写
  3. 修饰的基本类型变量和引用变量只能赋值一次,不可更改

static

static可以修饰成员变量(静态变量)、成员方法(静态方法)、代码块
静态变量/静态方法:所有该类的实例均共享
静态代码块:在类被加载时,执行一次。

接口

接口中声明的属性,只能是public static final修饰

内部类

静态内部类和非静态内部类和主类里面的属性、方法平起平坐。
匿名内部类和本地类则既可以放在主类里也可以放在主类的方法、代码块内部。

非静态内部类

public class xxx{
        class yyy{
            
        }
    }

外部类存在,才能创建内部类

new xxx().new yyy()

静态内部类

public class xxx{
        static class yyy{
            
        }
    }

不需要外部类存在,就能创建内部类

new xxx.yyy()

匿名内部类

匿名类指的是在声明一个类的同时实例化它,使代码更加简洁精练
通常情况下,要使用一个接口或者抽象类,都必须创建一个子类
有的时候,为了快速使用,直接实例化一个抽象类,并“当场”实现其抽象方法。
既然实现了抽象方法,那么就是一个新的类,只是这个类,没有命名。这样的类,叫做匿名类

public abstract class Abstract_Object {
    public abstract void abstract_method();
}
public class Main {
    public static void main(String[] args) {
        Abstract_Object object = new Abstract_Object() {
            @Override
            public void abstract_method() {
                System.out.println("abstract_method()");
            }
        };
        object.abstract_method();
    }
}

本地类

本地类可以理解为有名字的匿名类

public abstract class Abstract_Object {
    public abstract void abstract_method();
}
public class Main {
    public static void main(String[] args) {
        class NewObject extends Hero {
            public void abstract_method() {
                System.out.println("abstract_method()");
            }
        }
        NewObject object = new NewObject();
        object.abstract_method();
    }
}

默认方法

接口中可以写默认方法,实现类就可以不用重新实现了

public interface PersonMethod {
    void die();
    //接口方法的默认实现
    default public void eat(){
        System.out.println("eat()");
    }
}
public class Main {
    public static void main(String[] args) {
        //这里用个本地类来测试
        class Person implements PersonMethod{
            //eat()方法可以不用实现
            @Override
            public void die() {
                System.out.println("die");
            }
        }
        Person person = new Person();
        person.eat();
        person.die();
    }
}

异常处理

基本结构

	try{
            //todo
        }
        catch(FileNotFoundException e){
            e.printStackTrace();
        }
        finally{
            //不管有无异常,一定执行
        }

多异常处理

	try {
            //todo
        } catch (FileNotFoundException | ParseException e) {
            if (e instanceof FileNotFoundException)
                System.out.println("FileNotFoundException");
            if (e instanceof ParseException)
                System.out.println("ParseException");
            e.printStackTrace();
        }

throw与throws

throws表示该方法会把可能的异常向上层抛出,但不能总往上抛,总有一层要try、catch、finally
throw是在方法体中代码执行过程中手动抛出了异常。

 	private static void method1() {
        try {
            method2();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
    private static void method2() throws FileNotFoundException {
        throw new FileNotFoundException();
    }

异常与错误的分类

在这里插入图片描述

异常类

异常类的继承图:
在这里插入图片描述

IO流

分类

按方向分

到内存程序中,是输入流,从程序到文件啥的,是输出流。

按操作方式分

在这里插入图片描述

按操作对象分

在这里插入图片描述

选择

知道了 IO 流有这么多分类,那我们在使用的时候应该怎么选择呢?比如什么时候用输出流?什么时候用字节流?可以根据下面三步选择适合自己的流:

  1. 首先自己要知道是选择输入流还是输出流。这就要根据自己的情况决定,如果想从程序写东西到别的地方,那么就选择输入流,反之就选输出流;
  2. 然后考虑你传输数据时,是每次传一个字节还是两个字节,每次传输一个字节就选字节流,如果存在中文,那肯定就要选字符流了。
  3. 通过前面两步就可以选出一个合适的节点流了,比如字节输入流 InputStream,如果要在此基础上增强功能,那么就在处理流中选择一个合适的即可。

常见流的使用


import com.coderhao.User;
import java.io.*;
import java.security.PublicKey;

public class FileTest {
    public static void main(String[] args) {
        //相对路径
        File file = new File("a.exe");
        String path = file.getAbsolutePath();
        System.out.println(path);//在项目路径下,和src路径平行
        //绝对路径
        File file1 = new File("f:/a.exe");
        String path1 = file1.getAbsolutePath();
        System.out.println(path1);//f:\a.exe

        //字节流
        File file2 = new File("zijieliu.txt");
        //把流定义在try()里,try,catch或者finally结束的时候,会自动关闭,否则要手动关闭
        //先用哪个流就要把哪个流放前面
        try (FileOutputStream fileOutputStream = new FileOutputStream(file2)) {
            //输出
            String data = new String("hello有中文");
            byte[] bytes = data.getBytes("utf-8");
            fileOutputStream.write(bytes);

        } catch (IOException e) {
            e.printStackTrace();
        }

        try (FileInputStream fileInputStream = new FileInputStream(file2)) {
            //输入
            byte[] all = new byte[(int) file2.length()];
            fileInputStream.read(all);
            String s = new String(all, "utf-8");
            System.out.println(s);

        } catch (IOException e) {
            e.printStackTrace();
        }

        //字符流
        File file3 = new File("zifuliu.txt");
        try (FileWriter fileWriter = new FileWriter(file3)) {
            //输出字符
            String data = "字符流写出字符。";
            fileWriter.write(data);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //FileReader是不能手动设置编码方式的,为了使用其他的编码方式,只能使用InputStreamReader来代替
        //并且使用new InputStreamReader(new FileInputStream(f),"utf-8"); 这样的形式
        try (InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(file3), "utf-8")) {
            //输入字符
            char[] all = new char[(int) file3.length()];
            inputStreamReader.read(all);
            String s = new String(all);
            System.out.println(s);

        } catch (IOException e) {
            e.printStackTrace();
        }

        //缓存流
        //以介质是硬盘为例,字节流和字符流的弊端:在每一次读写的时候,都会访问硬盘。 如果读写的频率比较高的时候,其性能表现不佳。
        //缓存流在写入数据的时候,会先把数据写入到缓存区,直到缓存区达到一定的量,才把这些数据,一起写入到硬盘中去。按照这种操作模式,就不会像字节流,字符流那样每写一个字节都访问硬盘,从而减少了IO操作
        File file4 = new File("huancunliu.txt");
        //Socket编程中,尽量用PrintWriter取代BufferedWriter
        try (FileWriter fileWriter = new FileWriter(file4); PrintWriter printWriter = new PrintWriter(fileWriter)) {
            //输出
            printWriter.println("阿联军的啦aaa");
            printWriter.println("ashdkjh萨拉丁");

        } catch (IOException e) {
            e.printStackTrace();
        }

        try (FileReader fileReader = new FileReader(file4); BufferedReader bufferedReader = new BufferedReader(fileReader)) {
            //一行一行的读
            while (true) {
                String line = bufferedReader.readLine();
                if (line == null) {
                    break;
                }
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }


        //数据流
        //使用数据流的writeUTF()和readUTF()可以进行数据的格式化顺序读写
        File file5 = new File("shujuliu.txt");
        try (FileOutputStream fileOutputStream = new FileOutputStream(file5); DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream)) {
            //输出
            dataOutputStream.writeBoolean(true);
            dataOutputStream.writeUTF("安理会是的hello");
            dataOutputStream.writeDouble(13.58);
        } catch (IOException e) {
            e.printStackTrace();
        }

        try (FileInputStream fileInputStream = new FileInputStream(file5); DataInputStream dataInputStream = new DataInputStream(fileInputStream)) {
            //数据的读写要一一对应,有顺序的
            boolean b = dataInputStream.readBoolean();
            System.out.println(b);
            String s = dataInputStream.readUTF();
            System.out.println(s);
            double v = dataInputStream.readDouble();
            System.out.println(v);

        } catch (IOException e) {
            e.printStackTrace();
        }

        //对象流,指的是可以直接把一个对象以流的形式传输给其他的介质(序列化过程)
        //只能是implements Serializable 的类的对象
        File file6 = new File("duixiangliu.txt");
        try (FileOutputStream fileOutputStream = new FileOutputStream(file6); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream)) {
            User user1 = new User(001, "name1");
            User user2 = new User(002, "name2");
            objectOutputStream.writeObject(user1);
            objectOutputStream.writeObject(user2);

        } catch (IOException e) {
            e.printStackTrace();
        }

        try (FileInputStream fileInputStream = new FileInputStream(file6); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) {
            //写两个对象也只能读开头的一个对象
            User user = (User) objectInputStream.readObject();
            System.out.println(user);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        //追加
        //以上写入都是覆盖,如果要追加,可以1.改变文件指针 2.创建写入对象时第二个参数为true
        File file7 = new File("zhuijia.txt");
        for (int i = 0; i < 5; i++) {
            appendTest(file7);
        }
        try (FileReader fileReader = new FileReader(file7); BufferedReader bufferedReader = new BufferedReader(fileReader)) {


            while (true) {
                String s = bufferedReader.readLine();
                if (s == null) {
                    break;
                }
                System.out.println(s);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void appendTest(File file) {
        //打开一个写文件器,构造函数中的第二个参数true表示以追加形式写文件
        try (FileOutputStream fileOutputStream = new FileOutputStream(file, true); PrintWriter printWriter = new PrintWriter(fileOutputStream)) {
            printWriter.print("start中英文结合end");
            printWriter.print(12.55);
            //强制把缓存中的数据写入硬盘,无论缓存是否已满
            printWriter.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

集合

集合继承图

数组式的查找快,链表式的增删快,二叉树式的排序快,hash式的查找、增删都快。
HashSet无序,list有序。
在这里插入图片描述

常见使用

import com.coderhao.object.User;
import java.util.*;

public class CollectionTest {
    public static void main(String[] args) {

        ArrayList<User> users = new ArrayList<>();
        users.add(new User(001, "name1"));
        users.add(new User(002, "name2"));

        //迭代器iterator的for写法
        for (Iterator<User> iterator = users.iterator(); iterator.hasNext(); ) {
            User next = iterator.next();
            System.out.println(next);

        }

        //除了实现了List接口外,LinkedList还实现了双向链表结构Deque,可以很方便的在头尾插入删除数据
        final LinkedList<User> linkedList = new LinkedList<>();
        //尾部添加
        linkedList.add(new User(1, "name1"));
        //头部添加
        linkedList.addFirst(new User(0, "name-first"));
        System.out.println(linkedList);
        //查看头部
        System.out.println(linkedList.getFirst());
        //移除头部
        System.out.println(linkedList.removeFirst());
        System.out.println(linkedList);
        //LinkedList 除了实现了List和Deque外,还实现了Queue接口(队列)。Queue是先进先出队列FIFO
        //offer 在最后添加元素,poll 取出第一个元素,peek 查看第一个元素
        linkedList.offer(new User(2, "name2"));
        System.out.println(linkedList);
        //poll取出和remove类似
        System.out.println(linkedList.poll());
        System.out.println(linkedList);
        //peek查看不会移除
        System.out.println(linkedList.peek());
        System.out.println(linkedList);

        //与FIFO(先入先出的)队列类似的一种数据结构是FILO先入后出栈Stack
        //push放入栈顶,peek查看栈顶,pop取出栈顶
        class StackTest extends Stack {
        }

        //二叉树
        class Node {
            // 左子节点
            public Node leftNode;
            // 右子节点
            public Node rightNode;
            // 值
            public Object value;

            // 插入 数据
            public void add(Object v) {
                // 如果当前节点没有值,就把数据放在当前节点上
                if (null == value)
                    value = v;
                    // 如果当前节点有值,就进行判断,新增的值与当前值的大小关系
                else {
                    // 新增的值,比当前值小或者相同
                    if ((Integer) v - ((Integer) value) <= 0) {
                        if (null == leftNode)
                            leftNode = new Node();
                        leftNode.add(v);
                    }
                    // 新增的值,比当前值大
                    else {
                        if (null == rightNode)
                            rightNode = new Node();
                        rightNode.add(v);
                    }
                }
            }

            // 中序遍历所有的节点
            public List<Object> values() {
                List<Object> values = new ArrayList<>();

                // 左节点的遍历结果
                if (null != leftNode)
                    values.addAll(leftNode.values());
                // 当前节点
                values.add(value);
                // 右节点的遍历结果
                if (null != rightNode)
                    values.addAll(rightNode.values());
                return values;
            }
        }
        int[] ints = {2, 31, 24, 65, 23, 43};
        Node root = new Node();
        for (int i : ints) {
            root.add(i);
        }
        List<Object> values = root.values();
        System.out.println(values);//[2, 23, 24, 31, 43, 65]

        //HashMap要比List效率高,这是一种用空间换时间的思维方式得到的结果
        //Set中元素没顺序,HashSet底层就是HashMap,只是不用HashMap的v,只用k来存数据。

        //排序
        ArrayList<User> list = new ArrayList<>();
        list.add(new User(12, "name12"));
        list.add(new User(18, "name18"));
        list.add(new User(7, "name7"));
        Comparator<User> userComparator = new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
                //升序排列
                if (o1.id > o2.id) {
                    return 1;
                } else {
                    return -1;
                }
            }
        };
        Collections.sort(list, userComparator);
        System.out.println(list);

        //聚合操作lambda
        //通过聚合操作找出来id第三高的user名称
        String s = list
                .stream()
                .sorted((h1, h2) -> h1.id > h2.id ? -1 : 1)
                .skip(2)
                .map(h -> h.name)
                .findFirst()
                .get();
        System.out.println(s);
    }
}

注:lambda中的聚合操作

管道原:
把Collection切换成管道源很简单,调用stream()就行了。heros.stream()
但是数组却没有stream()方法,需要使用Arrays.stream(hs)
中间操作:
在这里插入图片描述
结束操作:
在这里插入图片描述

泛型

泛型

//泛型
public class FanxingTest<T> {
    LinkedList<T> values = new LinkedList<T>();

    public T getT(int index) {
        return values.get(index);
    }

    public void addT(T t) {
        values.add(t);
    }

    public static void main(String[] args) {
        FanxingTest<User> users = new FanxingTest<>();
        users.addT(new User(1, "name1"));
        System.out.println(users.getT(0));
    }
}

<? extends T>泛型

表示这是一个T泛型或者其子类泛型
该类的对象,一定是能转成T的

<? super T>泛型

表示这是一个T泛型或者其父类泛型

子父类泛型转换

子类可以转父类,但子类泛型不能转父类泛型。

举报

相关推荐

0 条评论