首页 > 编程语言 >Java开发常出错5颗星——空指针和异常

Java开发常出错5颗星——空指针和异常

时间:2023-06-11 23:56:26浏览次数:57  
标签:Java user 指针 User 颗星 new null out 出错

常犯指数5颗星

空指针

空指针概念及样例

  • 什么是空指针(java.lang.NullPointExcetion)?

空:内存地址

指针:引用

异常: 运行时

image-20230610215624459

private static class User{
        private String name;
        private String[] address;

        public void print(){
            System.out.println("This is User class!");
        }

        public String readBook(){
            System.out.println("user Read Imooc Escape");
            return null;
        }
    }

    public static class CustomException extends RuntimeException{

    }

    public static void main(String[] args) {
        //第一种情况,调用了空对象的实例方法
//        User user = null;
//        user.print();

        //第二种情况: 访问了空对象的属性
//        User user = null;
//        System.out.println(user.name);

        //第三种情况: 当数组是一个空对象的时候,取它的长度
//        User user = new User();
//        System.out.println(user.address.length);

        // 第四种情况: null 当做Throwable的值
//        CustomException exception = null;
//        throw exception;

        //第五种情况:方法的返回值是null,调用方直接去使用
        User user =new User();
        System.out.println(user.readBook().concat("Oracle"));

    }
  • 由于疏忽造成的空指针问题

image-20230610221409820

  • 针对与空指针的场景,有哪些方式可以避免发生
  1. 使用之前一定要初始化,或检查是否初始化
  2. 尽量避免在函数中返回Null,或给出详细的注释(良好的编程习惯)
  3. 外部传值, 除非有明确的说明(非Null),否则一定要及时判断

......

赋值时自动拆箱出现空指针

image-20230610221903335

image-20230610221942485

  • 为什么要有包装器类型

为了让基本类型具有对象的性质,丰富了基本类型的操作

常见的由于自动拆箱引发的空指针案例

    private static int add(int x,int y){
        return x+y;
    }
    private static boolean compare(long x, long y){
        return  x >= y;
    }
    public static void main(String[] args) {
//      1. 变量赋值自动拆箱出现的空指针
        // javac UnboxingNpe.java
        //javap -c UnboxingNpe.class
        Long count = null;
        long count_ = count;

        //2. 方法传参时自动拆箱引发的空指针问题
//        Integer left = null;
//        Integer right = null;
//        System.out.println(add(left,right));

        //3. 用于大小比较的场景
//        Long left = 10L;
//        Long right = null;
//        System.out.println(compare(left,right));
  • 自动拆箱引发的空指针
  1. 变量赋值自动拆箱出现的空指针

  2. 方法传参时自动拆箱出现的空指针

  • 规避自动拆箱引发空指针的建议
  1. 基本数据类型优于包装器类型,优先考虑使用基本类型
  2. 对于不确定的包装器类型,一定要校验是否是Null
  3. 对于值为Null的包装器类型,赋值为0

字符串、数组、集合在使用时出现空指针

  • 寻找代码案例中出现的场景

字符串使用equals时报空指针错误

对象数组虽然new出来了,但是如果没有初始化,一样会报空指针错误

List对象add null不报错,但是all All 不能添加null,否则会报空指针错误

 // 1. 字符串使用equals 可能会报空指针错误
        //false
//        System.out.println(stringEquals("xyz",null));
//        // npe
//        System.out.println(stringEquals(null,"xyz"));

        // 2. 对象数组new出来了, 但是元素没有初始化
//        User [] user = new User[10];
//        for (int i = 0; i != 10; i++) {
//            // 对user 进行初始化 就不会抛出异常了
//            user[i] = new User();
//            user[i].name = "imooc-"+i;
//        }
        //3. List对象 allAll 传递null会抛出空指针
        List<User> users = new ArrayList<>();
        User user = null;
        List<User> users_ = null;
        users.add(user);
        users.addAll(users_);

查看ArrayList AddAll源码,由于addAll调用了传入对象的toArray()方法,引起空指针

image-20230610225713826

使用Optional规避空指针时,需要注意些什么

  • Java8 提供的optional

是什么、定义了哪些方法、日常使用

  • 日常的使用方法有问题吗?

与直接判断是否null几乎一样,所以使用新的API意义不大,

认识到orElse、orElseGet、map等方法的妙用

get、ifPresent这样的方法更应该看做是私有(不要直接去使用)方法

  • 什么是Optional

image-20230610230039964

public class OptionalUsage {
    public static class User{
        private String name;

        public String getName() {
            return name;
        }
    }

    private static void isUserEqualsNull(){

        User user = null;
        if(user !=null){
            System.out.println("User is not null");
        }else{
            System.out.println("user is null");
        }

        Optional<User> optional = Optional.empty();
        if(optional.isPresent()){
            System.out.println("User is not null");
        }else {
            System.out.println("user is null");
        }
    }

    private static User anoymos(){
        return new User();
    }

    public static void main(String[] args) {
        // 没有意义的使用方法
        isUserEqualsNull();

        User user = null;
        Optional<User> optionalUser = Optional.ofNullable(user);
        // orElse 存在即返回, 空则提供默认值
        optionalUser.orElse(new User());
        //orElseGet 存在即返回, 空则由函数去返回
        optionalUser.orElseGet( ()->anoymos());
        //存在即返回, 否则抛出异常
        optionalUser.orElseThrow(RuntimeException::new);

        //存在才去做相应的处理
        optionalUser.ifPresent(u -> System.out.println(u.getName()));

        // map 可以对Optional中的对象执行某种操作,且会返回一个optional对象
        optionalUser.map(u -> u.getName()).orElse("anymos");

        //map是可以无线级联操作的
        optionalUser.map(u->u.getName()).map(name ->name.length()).orElse(0);
    }

异常

异常的概念

image-20230611111253018

  • 用户输出非法数据
  • 打开的文件不存在
  • 网络通信链接中断
  • JVM内存溢出
Java异常处理类

image-20230611111349676

Error:OOM

Exception:RunTimeException, 例如空指针异常,数组下标越界。属于非检查时异常。

Java异常处理事件原则
  • 使用异常,而不是返回码(或类似),因为异常会更加详细
  • 主动捕获检查性异常,并对异常信息进行反馈(日志或标记)
  • 保持代码整洁,一个方法中不要有多个try catch 或者嵌套的try catch(尝试将方法拆开解决)
  • 捕获更加具体的异常,而不是通用的Exception
  • 合理的设计自定义的异常类(更加明确的异常信息)
编码中常见的异常(并发修改、类型转换、每局查找及其解决办法)
  • 常见的案例有哪些

可迭代对象在遍历的同时做修改,则会报并发修改异常

类型转换不符合Java的继承关系,则会报类型转换异常

枚举在查找时,如果枚举值不存在,不会返回空,而是直接抛出异常

  • 直接使用for循环移除元素触发 并发修改异常
/**
     * 并发修改异常指遍历时进行修改: 快速失败机制 ,索引表中指针找不到元素
     * @param users userList
    */
    private static void concurrentModificationException(ArrayList<User> users){
        //直接使用for循环会触发并发修改异常
        for(User user:users){
            if(user.getName().equals("imooc")){
                users.remove(user);
            }
        }
        //使用迭代器则没有问题
//        Iterator<User> iter = users.iterator();
//        while (iter.hasNext()){
//            //next 必须在remove之前,否则触发快速失败
//            User user = iter.next();
//            if(user.getName().equals("imooc")){
//                iter.remove();
//            }
//
//        }
        // 更好的实现该功能应该使用jdk 1.8中filter方法进行过滤
    }

fail-fast原理

对于集合如List、Map,可以通过迭代器来遍历。而Iterator其实只是一个接口,需要调用具体的集合类中的内部类去实现Iterator并实现相关方法。ConcurrentModificationException都是在操作Iterator抛出的异常。

image-20230611220541687

通过调用checkForComodification比较“expectedModCount”和“modCount”大小,而在执行remove动作后,触发了modCount++操作。

image-20230611221049424

而在最初创建ArrayList时,expectModCount = modCount,因此触发了快速失败。

image-20230611221300731

详细讲解见博客【并发基础】Java中的fail-fast(快速失败)机制

对于枚举类查找的常见策略

  1. 使用try-catch包裹
  2. 对枚举类values进行遍历
  3. 静态map索引,只有一次map的索引过程
  4. 使用Google Guava Enums,需要引入相关的依赖
 // 1. 最普通、最简单的实现
//        try{
//            return StaffType.valueOf(type);
//        }catch (IllegalArgumentException ex){
//            return null;
//        }
//    2. 改进的实现, 但是效率不高
//        for(StaffType value:StaffType.values()){
//            if(value.name().equals(type)){
//                return value;
//            }
//        }
//        return null;
 // 3. 静态map索引, 只有一次map索引的过程
         private static final Map<String,StaffType> typeIndex = new HashMap<>(
            StaffType.values().length
    );
    static {
        for (StaffType value : StaffType.values()) {
            typeIndex.put(value.name(),value);

        }
    }
//        return typeIndex.get(type);
// 4. 使用Google Guava Enums,需要相关的依赖
	return Enums.getIfPresent(StaffType.class,type).orNull();
解决使用try finally的资源泄露隐患

image-20230611145352670

  • 资源释放:打开了资源,使用完之后手动释放(关闭)
  • 资源泄露:打开了资源,使用完之后由于某种原因没有手动释放
try finally的问题及改进方案

image-20230611150624733

对单个资源的操作基本不会有问题

当同时操作多个资源时,代码冗长,且存在资源泄露的风险

try-with-resource 不仅比try-finally方便,而且不容易出错

传统对资源的关闭

 private String traditionalTryCatch() throws IOException {
        //1. 单一资源的关闭
//        String line = null;
//        BufferedReader br = new BufferedReader(new FileReader(""));
//        try {
//            line = br.readLine();
//        } finally {
//            br.close();
//        }
//        return line;

        // 2. 多个资源的关闭
        //第一个资源
        InputStream in = new FileInputStream("");
        try {
            //第二个资源
            OutputStream out = new FileOutputStream("");
            try {
                byte[] buf = new byte[100];
                int n;
                while ((n = in.read(buf)) >= 0)
                    out.write(buf, 0, n);
            } finally {
                out.close();
            }
        } finally {
            in.close();
        }
        return null;
    }

这样做风险在于后一个关闭的异常会隐藏前一个出现的异常信息,代码冗长

java 7 引入的try-resources

 /**
     * java7 引入的try with resource 实现自动的资源关闭
     *
     * @return
     * @throws IOException
     */
    private String newTryWithResources() throws IOException {
        //1.单个资源的使用和关闭
//        try(BufferedReader br = new BufferedReader(new FileReader(""))){
//            return br.readLine();
//        }
        // 2.多个资源的使用与关闭
        try (FileInputStream in = new FileInputStream("");
             FileOutputStream out = new FileOutputStream("")
        ) {
            byte[] buffer = new byte[100];
            int n = 0;
            while ((n = in.read(buffer)) != -1) {
                out.write(buffer, 0, n);
            }
        }
        return null;
    }

只需要专注于自身业务逻辑即可,会自动对资源进行关闭操作,且不会覆盖异常信息。

标签:Java,user,指针,User,颗星,new,null,out,出错
From: https://www.cnblogs.com/shine-rainbow/p/17473876.html

相关文章

  • 银河麒麟安装JAVA JDK1.8
    卸载open-jdk1.首先查看系统是否自带Javarpm-qa|grepjavarpm-qa|grepjdkrpm-qa|grepgcj[root@localhost~]#rpm-qa|grepjavajava-1.8.0-openjdk-1.8.0.312.b07-10.ky10.x86_64javapackages-filesystem-5.3.0-3.ky10.noarchjava-11-openjdk-headless-11.0.13.9-6.k......
  • JavaWeb开发与代码的编写(十八)
    JavaWeb开发与代码的编写(十八)Filter(过滤器)Filter也称之为过滤器,它是Servlet技术中最激动人心的技术,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp,Servlet,静态图片文件或静态html文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控......
  • java刷题网站最近更新的面试题
    49个人中至少几个人生日是同一月?如何用3升和5升桶量取4升水?JVM逃逸分析默认是开启还是关闭?ZGC有缺点吗?JVM对Java的原生锁做了哪些优化?为什么wait(),notify()和notifyAll()必须在同步方法或者同步块中被调用?什么是锁消除和锁粗化?为什么代码会重排序?什么是自旋?你们线程池是怎......
  • Java中 List的遍历及三种遍历方法
    JavaList遍历方法及其效率对比packagecom.zbalpha.test;importjava.util.ArrayList;importjava.util.Iterator;importjava.util.List;publicclassListTest{publicstaticvoidmain(Stringargs[]){List<Long>lists=newArrayList<Long&g......
  • Java常用的几种JSON解析工具
    一、Gson:Google开源的JSON解析库1.添加依赖<!--gson--><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId></dependency><!--lombok--><dependency><groupId>org.proje......
  • java获取服务器ip地址的工具类
    参考:https://www.cnblogs.com/raphael5200/p/5996464.html代码实现importlombok.extern.slf4j.Slf4j;importjava.net.*;importjava.util.Enumeration;@Slf4jpublicclassIpUtil{publicstaticfinalStringDEFAULT_IP="127.0.0.1";/**......
  • java——微服务——spring cloud——Nacos——Nacos微服务配置拉取
       添加依赖:     添加bootstrap.yml文件    去除application.yml中和bootstrap.yaml中相同的配置项:      修改controller,验证配置更新功能            ......
  • Java11 Optional
    简介publicfinalclassOptional<T>{privatestaticfinalOptional<?>EMPTY=newOptional<>();privatefinalTvalue;privateOptional(){this.value=null;}……}Optional<T>是个容器,在java.util包中用......
  • Java反序列化之Commons-Collection篇04-CC4链
    <1>环境分析因为CommonsCollections4除4.0的其他版本去掉了InvokerTransformer不再继承Serializable,导致无法序列化。同时CommonsCollections4的版本TransformingComparator继承了Serializable接口,而CommonsCollections3里是没有的。这个就提供了一个攻击的路径jd......
  • java——微服务——spring cloud——Nacos——Nacos实现配置管理
        注意:只填写需要修改的,不是把配置文件全部复制进去                      ......