Java 面试题(持续更,最后一次更新时间:2021/4/2)

最后一次更新内容:

HashMap 源码解读

--------------------------目录直通车----------------------------

统计某段字符串中的某个字符串的个数?

 随机获取100以内的10个数且不重复并排序

 统计某段字符串中的某个字符串的个数

 HashMap 源码解读

你怎么理解Java中的异常处理? 

接口和抽象类的区别是什么? 

线程相关 

 String、StringBuilder、StringBuffer 的区别是什么?

说一说自己对于 synchronized 关键字的了解,自己在项目里是如何使用的?

你能手写一下 双重校验锁实现对象单例 的代码吗?

HashSet  是如何保证不重复的

 你讲一下 HTTP 协议吧

为什么要有数据类型?

创建对象的方式有几种?分别是?

你讲一下 Java 中 new 一个对象的执行过程及类的加载顺序

HashSet 如何保证元素的唯一性?存储自定义对象保证元素的唯一性?

Java web 域对象作用?有哪些?(Cookie与Session)

Java中增强一个类中的方法有几种方式?分别是?

Mybatis和JDBC的区别

谈谈 synchronized 和 ReenTrantLock 的区别?

用 SQL 完成如图要求的数据展示

java中,3*0.1与0.3是否相等?4*0.1与0.4是否相等?

== 和 equals 的区别是什么?

final 在 java 中有什么作用?

String str="i" 与 String str=new String(“i”)一样吗?

java 中的 Math.round(-1.5) 等于多少?


        为了方便阅读JDK的源码,答案和讲解在代码的注释里边。

很多的面试题原图我会共享到群,欢迎大家进群吹水(183579482)。

统计某段字符串中的某个字符串的个数?

/**
 * 统计某段字符串中的某个字符串的个数
 * 解题关键:String.indexOf()
 *
 * @author DJun
 */
public class FindKeyWords {
    public static void main(String[] args) {
        String text = "An array is a data struct. Trying to find array which is the keyword";
        int arrayCount = 0;
        int index = -1;
        String arrayStr = "array";

        index = text.indexOf(arrayStr);

        while (index >= 0) {
            ++arrayCount;
            index += arrayStr.length();
            index = text.indexOf(arrayStr, index);
        }
        System.out.println("array数量为" + arrayCount);
    }
}

 随机获取100以内的10个数且不重复并排序


/**
 * 随机获取100以内的10个数且不重复并排序
 * <p>
 * 解题关键:Set 的特性有无序性、不可重复性。无序性不等于随机性,位置是按照哈希值来定的。
 * 要求添加进Set中的元素所在的类,一定要重写equals()和 hashCode()方法,进而保持Set的不可重复性。
 *
 * @author DJun
 */
public class NoRepeatRandom {
    public static void main(String[] args) {
        Random random = new Random();
        HashSet<Integer> numSet = new HashSet<>();
        List<Integer> list = new ArrayList<>();
        
        while (numSet.size() < 10) {
            int num = random.nextInt(100);
            numSet.add(num);
        }

        // 1、方法一
        TreeSet<Integer> ts = new TreeSet(numSet);
        /* 这边非常感谢id名为 _易 的朋友指出: TreeSet的构造函数里面调用了addAll()方法,而addAll()方法会进行排序。
          我翻阅的一下TreeSet的源码
          调用的构造函数为
             public TreeSet(Collection<? extends E> c) {
                this();
                addAll(c);
            }
          addAll() 调用了 SortedSet<? extends E> set = (SortedSet<? extends E>) c;实现了排序
         */

//        ts.comparator();
        System.out.println(ts);

        // 2、方法二
        Iterator iterator = numSet.iterator();
        while (iterator.hasNext()) {
            list.add((Integer) iterator.next());
        }
        Collections.sort(list);
        System.out.println(list);

    }
}

 统计某段字符串中的某个字符串的个数

/**
 * 统计某段字符串中的某个字符串的个数
 * 解题关键:String.indexOf()
 *
 * @author DJun
 */
public class FindKeyWords {
    public static void main(String[] args) {
        String text = "An array is a data struct. Trying to find array which is the keyword";
        int arrayCount = 0;
        int index = -1;
        String arrayStr = "array";

        index = text.indexOf(arrayStr);

        while (index >= 0) {
            ++arrayCount;
            index += arrayStr.length();
            index = text.indexOf(arrayStr, index);
        }
        System.out.println("array数量为" + arrayCount);
    }
}

 HashMap 源码解读

内容较多,耐心看完。学习还需脚踏实地,循序渐进。

HashMap源码解读传送门(点击跳转)。

你怎么理解Java中的异常处理? 

/**
 * 你怎么理解Java中的异常处理?
 *  在 Java 里面异常类最终都继承于 Throwable 类
 *  Throwable: 有两个重要的子类: Exception(异常) 和 Error(错误)
 *
 *  Error : 程序无法处理的错误,足见比较严重的问题,
 *          大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。
 *
 *  Exception :是程序本身可以处理的异常。Exception 类有一个重要的子类 RuntimeException。
 *                  RuntimeException 异常由 Java 虚拟机抛出。
 *
 * @author DJun
 * @date 2019/10/4
 */
public class ExceptionPrinciple {
        Throwable t ;
}

接口和抽象类的区别是什么? 

/**
 * 接口和抽象类的区别是什么?
 * 1、一个类可以实现多个接口,但却只能实现一个抽象类
 * 2、接口默认的方法是public,且不能在接口中实现方法。抽象类却可以有非抽象的方法。
 * 3、一个类实现接口的话要实现接口类中的所有方法,而抽象类不一定。
 * 4、接口不能用 new 来实例化,但可以声明。
 * 5、接口中的实例变量默认是 final 类型的,而抽象类中则不一定。
 *
 * @author DJun
 * @date 2019/10/4
 */
public class InterfaceAndAbstract {
}

线程相关 


/**
 * 如何理解多线程?
 * CPU在进程中做时间片的切换。
 * 
 * 1、最基本的创建线程的两种方式:继承Thread 、实现 Runnable 接口。
 *   当然还有实现 Callable 和线程池的方式
 * 2、这两者的区别是什么?
 *  (1)如果多个线程需要处理同一份资源,那么就用 runnable,因为在
 *  Java里面,类是单继承的,用这种实现接口的方式恰好可以解决单继承的
 *  局限性。最终还是需要把实现了Runnable 接口类的对象传入Thread,用Thread的对象来操作线程。
 *
 * (2)继承Thread的类可以直接操作线程,不需要再创建一个Thread的对象。
 *
 * 3、死锁
 *   死锁是多线程操作同一块资源时,都在等待对方执行完毕而陷入无限等待的状态。
 *
 * 4、乐观锁、悲观锁(一般数据库使用)
 *  (1)乐观锁总假设最好的情况,每次拿数据的时候,都认为对方不会修改数据,所以不会上锁,
 *  但是在更新这份数据的时候,会判断在此期间有没有别人修改这个数据,一般用版本号和CAS来实现。
 *  (2)悲观锁反之,第一个人来拿数据的时候,就会上锁,直到执行完成之后,才允许别人来拿数据。
 *
 * 5、wait 与 sleep()的区别?
 *  sleep() 来自 Thread类,wait()来自 Object 类,
 *  调用 sleep(),不会释放对象锁,不会让出CPU,需要指定睡眠的时间,时间一到才苏醒。
 *  调用 wait() 却会释放对象锁,可以让其他线程占用 CPU,通过notify()或notifyAll()来唤醒。
 *
 * 6、ThreadLocal 和 Synchronized 的区别
 *  (1)ThreadLocal是线程局部变量,局限于线程内部的变量,也就是说它是属于线程自身所有,不在多个线程间共享。
 *  Java提供 ThreadLocal 类来支持线程局部变量,是一种实现线程安全的方式。ThreadLocal 为每一个线程都提供了变量的副本,
 *  使得每个线程在某一时间访问到的并不是同一个对象,以此隔离多线程对数据的数据共享。
 *
 *  (2)synchronized 相当于悲观锁,利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。
 *
 *
 * @author DJun
 * @date 2019/10/4
 */
public class Multithreading {
        // 点击进去看源码
        Thread thread;
        ThreadLocal threadLocal = new ThreadLocal();
}

 String、StringBuilder、StringBuffer 的区别是什么?


/**
 * String、StringBuilder、StringBuffer 的区别是什么?
 * 1、String 线程安全的原因是:private final char value[],其它两个没有用 final 修饰符,
 * 2、StringBuilder 和 StringBuffer 本质上没有区别,
 * 只是StringBuilder去掉了线程安全那部分,用于字符串拼接不需要线程安全(用于单线程),速度更快。
 * 3、所以 StringBuffer 是线程安全的(用于多线程),因为加了同步锁。
 *
 * == 与 equals 的区别是什么?
 * 都是判断地址是否相同,也就是说判断两个对象是不是同一个对象(基本数据类型==比较的是值,引用数据类型==比较的是内存地址)
 * 但 equals 一般有两种使用情况:
 *
 * 1、类没有重写 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象中的 equals 比较的对象里面的内容是否相等。
 * 2、类重写了 equals() 方法。一般,我们都重写 equals() 方法来两个对象的内容相等;若它们的内容相等,
 *    则返回 true (即,认为这两个对象相等)。
 *
 * 说明:
 *  1、String 中的 equals 方法是被重写过的,因为 object 的 equals 方法是比较的对象的内存地址,
 *     而 String 的equals 方法比较的是对象的值。
 *  2、当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,
 *     如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象。
 *
 * @author DJun
 * @date 2019/10/4
 */
public class StringBuilderAndBuffer {
    // 点击进去看源码
    String str;
    StringBuilder stringBuilder;
    StringBuffer stringBuffer;

    public static void main(String[] args) {
        String a = new String("ab"); // a 为一个引用
        String b = new String("ab"); // b 为另一个引用,对象的内容一样
        String aa = "ab"; // 放在常量池中
        String bb = "ab"; // 从常量池中查找
        if (aa == bb) // true
            System.out.println("aa==bb");
        if (a == b) // false,非同一对象
            System.out.println("a==b");
        if (a.equals(b)) // true
            System.out.println("aEQb");
        if (42 == 42.0) { // true
            System.out.println("true");
        }
    }
}

说一说自己对于 synchronized 关键字的了解,自己在项目里是如何使用的?


/**
 * 说一说自己对于 synchronized 关键字的了解?
 * Synchronized 解决的是多线程访问资源的同步性,可以给Synchronized 修饰的方法
 * 或代码块加锁,保证在任意时刻都只能有一个线程执行。加的这个锁属于重量级锁,效率
 * 低下。因为在底层用的监视器锁需要用到操作系统的 Mutex Lock 来实现,Java 的线
 * 程是映射到操作系统的原生线程之上的。如果要挂起或者唤醒一个线程就需要操作系统来
 * 帮忙完成,然而操作系统实现把线程从操作态转换成内核态的期间会需要相对较长的时间,
 * 时间成本较高,这也是为什么早期的 synchronized 效率低的原因。庆幸的是在 Java6
 * 之后Java 官方对从 JVM 层面对 synchronized 较大优化,所以现在的 synchronized锁
 * 效率也优化得很不错了。JDK1.6 对锁的实现引入了大量的优化,如自旋锁、适应性自旋锁、
 * 锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。
 *
 * 在项目中是如何使用的?
 *  synchronized 关键字最主要的三种使用方式:
 *  修饰实例方法,作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁
 *  修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁 。也就是给当前类加锁,会作
 *  用于类的所有对象实例,因为静态成员不属于任何一个实例对象,是类成员( static 表明这是该类的一个静态
 *  资源,不管 new 了多少个对象,只有一份,所以对该类的所有对象都加了锁)。所以如果一个线程 A 调用一个实
 *  例对象的非静态 synchronized 方法,而线程 B 需要调用这个实例对象所属类的静态 synchronized 方法,是允
 *  许的,不会发生互斥现象,因为访问静态 synchronized 方法占用的锁是当前类的锁,而访问非静态
 *  synchronized 方法占用的锁是当前实例对象锁。
 *  修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。 和 synchronized 方
 *  法一样,synchronized(this)代码块也是锁定当前对象的。synchronized 关键字加到 static 静态方法和
 *  synchronized(class)代码块上都是是给 Class 类上锁。这里再提一下:synchronized 关键字加到非 static 静态
 *  方法上是给对象实例上锁。另外需要注意的是:尽量不要使用 synchronized(String a) 因为 JVM 中,字符串常量
 *  池具有缓冲功能!
 *
 *
 *
 * @author DJun
 * @date 2019/10/5
 */
public class SynchronizedUsing {
    private synchronized void method1() throws InterruptedException {
        System.out.println("method1 begin at:" + System.currentTimeMillis());
        Thread.sleep(6000);
        System.out.println("method1 end at:" + System.currentTimeMillis());
    }

    private synchronized void method2() throws InterruptedException {
        while (true) {
            System.out.println("method2 running");
            Thread.sleep(200);
        }
    }

    private static SynchronizedUsing instance = new SynchronizedUsing();

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            try {

                instance.method1();

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 1; i < 4; i++) {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Thread1 still alive");
            }
        });

        Thread thread2 = new Thread(() -> {
            try {
                instance.method2();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        thread1.start();
        thread2.start();
    }
}

你能手写一下 双重校验锁实现对象单例 的代码吗?

简单通俗的说一下单例里面的 懒汉模式 和 饿汉模式:前者就是懒,在自己的类里面创建一个静态的当前类的对象,外部直接调用改方法即可,不需要在new。后者,饥渴无比,来者都欢迎,但是你来的时候没有对象,则才new 一个,如果有对象了,就不让你进来了。


/**
 * 你能手写一下双重校验锁实现对象单例(线程安全)的代码吗?
 *
 * 注意:在这里使用 volatile 修饰是非常有必要的,解决数据易变失效问题
 *
 * 通过下面这个实例来说明 volatile 的作用:
 * (1)下面这个类是不安全的 因为get和set方法都是在没有同步的情况下进行的。
 *  如果线程1调用了set方法,那么正在调用的get的线程2可能会看到更新后的value值,也可能看不到。
 * public class MutableInteger {
 *     private int value;
 *     public int get(){
 *         return value;
 *     }
 *     public void set(int value){
 *         this.value = value;
 *     }
 * }
 * 将value声明为volatile变量:private volatile int value; 即可解决上面这个问题。
 *
 * (2)下面这个段代码其实是分为三步执行:
 * 1. 为 uniqueInstance 分配内存空间
 * 2. 初始化 uniqueInstance
 * 3. 将 uniqueInstance 指向分配的内存地址
 *
 * 但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1->3->2。指令重排在单线程环境下不会出先问题,但是在
 * 多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用
 * getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被
 * 初始化。使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。
 *
 * @author DJun
 * @date 2019/10/6
 */
public class VolatileSingleton {

    private volatile static VolatileSingleton uniqueInstance;

    private VolatileSingleton() {}

    public static VolatileSingleton getUniqueInstance() {
        //先判断对象是否已经实例过,没有实例化过才进入加锁代码
        if (uniqueInstance == null) {
            // 类对象加锁
            synchronized (VolatileSingleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new VolatileSingleton();
                }
            }
        }
        return uniqueInstance;
    }

}

HashSet  是如何保证不重复的

HashSet add ()元素时,判断元素是否存在的依据,不仅要比较 hash 值,同时还要结合 equles 方法比较。 HashSet 中的 add ()方法会使用 HashMap add ()方法。以下是 HashSet 部分源码:

private static final Object PRESENT = new Object();
private transient HashMap<E,Object> map;
public HashSet() {
    map = new HashMap<>();
}
public boolean add(E e) {
    return map.put(e, PRESENT)==null; 
}

HashMap key 是唯一的,由上面的代码可以看出 HashSet 添加进去的值就是作为 HashMap key。所以不会重复( HashMap 比较 key 是否相等是先比较 hashcode 在比较 equals )。

 你讲一下 HTTP 协议吧


/**
 * 你可以讲一下 HTTP 协议吗?
 *
 * 这个问题一听就很大,让求职者自由发挥。因为我们是面的 Java开发,所以这个问题正确的回答姿势为:
 *  顺序我梳理为: 什么是 Http 协议 -> 由什么组成 -> 网络传输过程 -> 特征 -> 请求方式
 *  扩展一点就讲: 常见Http协议状态 -> Http 和 Https 的优缺点 -> Http协议首部字段是什么 -> Http协议中Http1.0与1.1区别
 *
 *  1、什么是 Http 协议。客户端与服务端之间的数据传输的格式规范,简称超文本传输协议。
 *  2、由什么组成。客户端的请求报文(Request)和服务端的响应报文(Response)组成
 *    Request 和 Response 都由三个部分组成
 *     Request   (1)请求行(包含请求方法、URI、HTTP版本信息)
 *               (2)请求首部字段
 *                 通用首部字段 : Date:创建报文时间、Connection:连接的管理、Cache-Control:缓存的控制、Transfer-Encoding:报文主体的传输编码方式
 *                 Host:请求资源所在服务器、Accept:可处理的媒体类型、Accept-Charset:可接收的字符集、Accept-Encoding:可接受的内容编码、Accept-Language:可接受的自然语言
 *               (3)请求内容实体
 *
 *     Response  (1)状态行(包含HTTP版本、状态码、状态码的原因短语)
 *               (2)响应首部字段
 *                  Accept-Ranges:可接受的字节范围、Location:令客户端重新定向到的URI、Server:HTTP服务器的安装信息
 *               (3)响应内容实体
 *                  实体首部字段:  Allow:资源可支持的HTTP方法、Content-Type:实体主类的类型、Content-Encoding:实体主体适用的编码方式、Content-Language:实体主体的自然语言
 *                               Content-Length:实体主体的的字节数、Content-Range:实体主体的位置范围,一般用于发出部分请求时使用
 *
 *
 *
 * 举个通过 HTTP1.1 的 GET 请求获取 http://img.mukewang.com/562f25980001b1b106000338.jpg 的例子
 *   GET /562f25980001b1b106000338.jpg HTTP/1.1
 *   Host img.mukewang.com
 *   User-Agent  Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36
 *   Accept  image/webp,image/*,* /*;q=0.8
 *   Referer http://www.imooc.com/
 *   Accept-Encoding gzip,deflate,sdch
 *   Accept-Language zh-CN,zh;q=0.8
 *
 * 3、网络传输过程。HTTP 协议 属于 TCP/IP 协议,TCP/UDP 属于 OSI 模型中的传输层,IP 属于网络层,HTTP 属于应用层。
 *
 *    补充说一点 TCP和 UDP 的区别?
 *    TCP 需要建立连接才能传输数据,当然肯定会有连接不上的情况,所以就有了最低限度的三次握手(很大程度上保证了连接的可靠性)。
 *    UDP 不需连接,不可靠。UDP传送数据前并不与对方建立连接,对接收到的数据也不发送确认收到的信号,发送端不知道数据是否能被正确接收,
 *    所以也不再重发。但是相比之下,UDP的开销更小,数据传输速度快,效率高,因为不必进行收发数据的确认,所以 UDP 的实时性更好。
 *
 * 服务器先初始化一个socket,与端口绑定,对端口进行监听,调用阻塞,等待客户端的连接,初始化客户端的socket,与服务器的socket连接,需要经过三次握手:
 *    关键字:SYN(synchronous建立联机)、ACK(acknowledgement 确认)
 *    第一次握手:客户端发送 SYN 包(SYN = j)到服务器,并进入 SYN_SEND 状态,等待服务器确认;
 *    第二次握手:服务器收到 SYN 包,必须确认客户的 SYN(ACK = j+1),同时自己也发送一个 SYN 包(SYN = k),即 SYN+ACK 包,此时服务器进入SYN_RECV状态;
 *    第三次握手:客户端收到服务器的 SYN+ACK 包,向服务器发送确认包 ACK(ACK=k+1),此包发送完毕,客户端和服务器进入 ESTABLISHED 状态,完成三次握手
 * 最后一步就是客户端与服务端的数据交互。
 *
 * 4、特征。
 *  (1)支持客户/服务器模式;(2)简单快速;(3)灵活;(4)无连接;(5)无状态。
 *   这里强调一下什么是无状态! 这个是指 HTTP 协议对于事务处理没有记忆能力。
 *   可以通过 Cookie 和 Session 解决这个无状态的问题。
 *
 * 5、请求方式。
 * GET: 用于请求访问已经被URI(统一资源标识符)识别的资源,可以通过URL传参给服务器
 * POST:用于传输信息给服务器,主要功能与GET方法类似,但一般推荐使用POST方式。
 * PUT: 传输文件,报文主体中包含文件内容,保存到对应URI位置。
 * HEAD: 获得报文首部,与GET方法类似,只是不返回报文主体,一般用于验证URI是否有效。
 * DELETE:删除文件,与PUT方法相反,删除对应URI位置的文件。
 * OPTIONS:查询相应URI支持的HTTP方法。
 *
 * 6、常见Http协议状态。
 *  200:请求被正常处理
 *  204:请求被受理但没有资源可以返回
 *  206:客户端只是请求资源的一部分,服务器只对请求的部分资源执行GET方法,相应报文中通过Content-Range指定范围的资源。
 *  301:永久性重定向
 *  302:临时重定向
 *  303:与302状态码有相似功能,只是它希望客户端在请求一个URI的时候,能通过GET方法重定向到另一个URI上
 *  304:发送附带条件的请求时,条件不满足时返回,与重定向无关
 *  307:临时重定向,与302类似,只是强制要求使用POST方法
 *  400:请求报文语法有误,服务器无法识别
 *  401:请求需要认证
 *  403:请求的对应资源禁止被访问
 *  404:服务器无法找到对应资源
 *  500:服务器内部错误
 *  503:服务器正忙
 *
 * 7、Http 和 Https 的优缺点。
 *   Https 由两部分组成:HTTP + SSL / TLS 也就是在HTTP上又加了一层处理加密信息的模块。服务端和客户端的信息传输都会通过TLS进行加密。
 *     缺点:加载页面的时间变长,影响缓存,增加数据的开销和功耗,安全性还是有缺陷(比如服务器劫持、SSL 证书的信用链体系并不安全)。
 *
 *   Http 的话一个是无法验证报文完整性,可能被篡改;二个是不验证通信方身份,可能遭到伪装;三个是通信使用明文不加密,易被抓包分析。
 *   优点:相对于 Https 而言。
 *
 * 8、Http协议中 Http1.0 与 1.1 区别。
 * (1)缓存处理。在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,
 *      HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match
 *      等更多可供选择的缓存头来控制缓存策略。
 *
 * (2)带宽优化及网络连接的使用。HTTP1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,
 *     而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,
 *     它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。
 *
 * (3)错误通知的管理。在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;
 *     410(Gone)表示服务器上的某个资源被永久性的删除。
 *
 * (4)Host头处理。在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。
 *      但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。
 *      HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。
 *
 * (5)长连接。HTTP 1.1支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,
 *      减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启Connection: keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。
 *
 * @author DJun
 * @date 2019/10/12
 */
public class WhatHttp {
    // 什么是Http协议
}

为什么要有数据类型?

下面虽然才两句话,但是作者花了很长的时间来回答这个问题,问了不少大佬,百度也看了不少的相关文章。

首先,一定要注意:任何编程语言(除了汇编,汇编只规定数据的字长),都有自己的数据类型,只是强弱类型之分而已。

1、合理利用计算机内存空间。如果没有数据类型,能否正常计算先不说,那么应该给变量开辟多少个字节空间呢?肯定只能开大不能小吧。比如 double 类型占64位(bit),byte 占8位,那么就只能都给大家开64位的空间了。再说不定义数据类型正常运算的情况,现在对于数据之间的差别只有位的不同而已,如果定义 一 个字符变量,首先要去申请 一 个8位的内存空间,再去按ASCII码对其进行赋值,这样对编程人员来说是很困难的。

2、操作 一 个任意形式的变量,是很不好掌握且容易出错的,所以引入数据类型的概念,限制人的操作,从而降低操作难度和出错率。所以如果定义了数据类型和数据类型之间的运算,对程序员是非常友好的。

创建对象的方式有几种?分别是?

import java.io.*;
import java.util.HashSet;
import java.util.Set;

/**
 * 创建对象的方式有几种?分别是?
 *
 * @author DJun
 * @date 2019/10/13
 */
public class CreateObject {
    public static void main(String[] args) {
        /*
         * 1、众所周知通过 new 来调用构造函数的方式创建对象
         */
        Staff staff = new Staff();
        staff.setId(1L);
        staff.setName("通过 new 来创建对象");
        System.out.println(staff);


        /*
         * 2、通过反射(newInstance())来创建对象,还是比较常用的!!!
         */
        Staff staff2;
        {
            try {
                // 这里的路径注意下:我的包名是 package interview;这个下面的Staff这个类
                staff2 = (Staff) Class.forName("interview.Staff").newInstance();
                staff2.setId(2L);
                staff2.setName("通过newInstance()来创建对象");
                System.out.println(staff2);
            } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
                e.printStackTrace();
            }
        }

        /*
         * 3、通过object类的clone方法
         */
        try {
            CloneTest cloneTest = (CloneTest) Class.forName("interview.CloneTest").newInstance();
            System.out.println(cloneTest);

            CloneTest cloneTest1 = new CloneTest(3, "通过Clone()来创建对象");
            System.out.println(cloneTest1);

            CloneTest cloneTest2 = (CloneTest) cloneTest1.clone();
            System.out.println(cloneTest2);

            System.out.println(cloneTest1 == cloneTest2);
            System.out.println(cloneTest1.equals(cloneTest2));
            /*
             * == 与 equals 的区别是什么?
             * 这个问题在讲StringBuilder和StringBuffer的时候讲过了,这里再简单的讲一下吧。
             *
             * 两种方式都是判断地址是否相同,也就是说判断两个对象是不是同一个对象(注意:基本数据类型==比较的是值,引用数据类型==比较的是内存地址)
             * 对象中的 equals 比较的对象里面的内容是否相等。
             *
             */
        } catch (InstantiationException | IllegalAccessException | ClassNotFoundException | CloneNotSupportedException e) {
            e.printStackTrace();
        }


        /*
         * 4、反序列化创建对象。
         * 一起回顾一下序列化的概念吧:
         * 序列化(serialization)在计算机科学的数据处理中,是指将数据结构或对象状态转换成可取用格式(例如存成文件,
         *  存于缓冲,或经由网络中发送),以留待后续在相同或另一台计算机环境中,能恢复原先状态的过程。
         *  依照序列化格式重新获取字节的结果时,可以利用它来产生与原始对象相同语义的副本。对于许多对象,像是使用大量引用的复杂对象,
         *  这种序列化重建的过程并不容易。面向对象中的对象序列化,并不概括之前原始对象所关系的函数。这种过程也称为对象编组(marshalling)。
         *  从一系列字节提取数据结构的反向操作,是反序列化(也称为解编组、deserialization、unmarshalling)。
         *
         * 序列化在计算机科学中通常有以下定义:
         *  对同步控制而言,表示强制在同一时间内进行单一访问。
         *  在数据储存与发送的部分是指将一个对象存储至一个存储介质,例如文件或是存储器缓冲等,或者透过网络发送数据时进行编码的过程,
         *  可以是字节或是XML等格式。而字节的或XML编码格式可以还原完全相等的对象。这程序被应用在不同应用程序之间发送对象,
         *  以及服务器将对象存储到文件或数据库。相反的过程又称为反序列化。
         *
         * 在Java里面,大多数的存储对象的格式都用的JSON吧,用一句简洁通俗的话来说,就是对象转JSON就是序列化,反之就是反序列化。
         *
         */
        Set<String> set = new HashSet<>();
        set.add("4");
        set.add("通过反序列化创建对象");
        System.out.println(set);

        try {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("set.obj"));
            oos.writeObject(set);
            set.clear();
            System.out.println(set);

            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("set.obj"));
            set = (Set<String>) ois.readObject();
            System.out.println(set);


        } catch (IOException | ClassNotFoundException e) {
            System.out.println(e.getMessage());
        }


    }
}

class Staff {
    private Long id;
    private String name;

    public Staff() {
    }

    public Staff(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Staff{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

class CloneTest implements Cloneable {
    private int id;
    private String name;

    public CloneTest() {
    }

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "CloneTest{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof CloneTest)) {
            return false;
        } else {
            return this.id == ((CloneTest) obj).id && this.name.equals(((CloneTest) obj).name);
        }
    }

}

你讲一下 Java 中 new 一个对象的执行过程及类的加载顺序


/**
 * 你讲一下 Java 中 new 一个对象的执行过程及类的加载顺序
 * 面试官想听的无非就是:new 的类中的 静态/动态变量、初始化块、父类/子类构造函数的执行顺序。
 *
 * @author DJun
 * @date 2019/10/13
 */
public class NewClassExecuteOrder {
    /**
     * 我们作为 Java 开发人员,当然不能只是为了面试而死记硬背,所以我举了下面这个例子一起学习new的这个过程。
     * Leader leader = new Leader(); 代码做了什么事?
     * 检查 new 指令的参数是否能在常量池中定位到一个类到符号引用,并检查这个符号引用代表到类是否已经被加载、解析和初始化。
     * 如果没有,那么 Javac.exe 把 Leader 这个类编译成.class字节码文件,
     *
     * 接着检查这个类是否存在父类,有父类的话,ClassLoader(类加载器)将父类的字节码文件加载到 JVM,创建一个 Java.lang.class 对象,
     * 为父类的静态属性分配空间(对象所需内存的大小在类加载完成后便可完全确定,为对象分配空间的任务等同于把一块确定大小的内存从Java堆中划分出来)并赋于初值,执行父类静态初始化块。
     *
     * 然后加载子类,类加载器将子类的 Class文件载入JVM,为子类的静态属性分配空间并赋于初值,执行子类的静态的内容;
     *
     * 接着加载父类构造器:
     * (1)初始化父类的非静态属性并赋于初值;
     * (2)执行父类的非静态代码块;
     * (3)执行父类的构造方法;
     *
     * 接在子类构造器方法:
     * (1)初始化子类的非静态属性并赋于初值
     * (2)执行子类的非静态代码块;
     * (3)执行子类的构造方法.
     *
     * 总结:执行顺序为 父类静态初始化块 > 子类静态初始化块 > 父类实例初始化块 > 父类构造器 > 子类实例初始化块 > 子类构造器
     */

    // 静态变量
    public static int num;

    //静态初始化块
    static {
        System.out.println(num + ":静态初始化块");
    }

    //实例初始化块
    {
        System.out.println(num + ":实例初始化块");
    }

    //构造方法
    public NewClassExecuteOrder() {
        System.out.println(num + ":构造函数初始化");
        num++;
    }

    //静态方法
    public static void getInstance() {
        System.out.println(num + ":静态方法初始化");
    }

    public static void main(String[] args) {
       new NewClassExecuteOrder();
       new NewClassExecuteOrder();
    }
}

HashSet 如何保证元素的唯一性?存储自定义对象保证元素的唯一性?

/**
 * HashSet 如何保证元素的唯一性?存储自定义对象保证元素的唯一性?
 *  下面点进去看一下 HashSet 的构造器: public HashSet() { map = new HashMap<>();},
 *  发现 HashSet 实际上是一个 HashMap (这里再罗嗦一点:HashMap的key是实现的Set接口,value实现的是Collection接口)实例,
 *  对象存入 HashSet 时,实际上是存放在 HashMap 的 Key 上的,Value 统一的一个固定对象 private static final Object PRESENT = new Object();
 *
 *
 * 结合上面的问题和这个问题一起回答,HashSet 和 HashMap 的区别是什么?
 *  (1)HashSet实现的是 Set 接口,HashMap 实现的是 Map 接口。
 *  (2)HashSet 用于存储对象,HashMap 存放键值对。
 *  (3)HashMap 的 Key 来计算 hashCode 的,源码如下:
 *       关于在哪儿判断 Key 是否重复的,那么就要看添加元素的方法是怎么实现的
 *       public V put(K key, V value) {
 *         return putVal(hash(key), key, value, false, true);
 *      }
 *
 *     HashSet 的 add() 方法,调用的 map 的 put() 方法,
 *     public boolean add(E e) {
 *         return map.put(e, PRESENT)==null;
 *     }
 *
 *  如果是在 HashMap 中调用 put,首先会判断 key 是否存在,如果 key 存在则修改 value 值,
 *  如果 key 不存在这插入这对 key-value。而在 set 中,value 值没有用,也就不存在修改 value 值的说法,
 *  因此往 HashSet 中添加元素的时候,只需要判断 key 是否重复,
 *
 *  所以判断 key 是否存在就要重写 equals() 和 hashCode() 方法,当向 Set 中添加对象时,
 *  首先调用此对象所在类的 hashCode() 方法,计算次对象的哈希值,此哈希值决定了此对象在 Set 中存放的位置;
 *  若此位置没有被存储对象则直接存储,若已有对象则通过对象所在类的 equals() 比较两个对象是否相同,相同则不能被添加。
 *
 *
 * @author DJun
 * @date 2019/10/14
 */
public class HashSetPrincipe {
    HashSet hashSet;
    HashMap hashMap;
    Set set;

}

Java web 域对象作用?有哪些?(Cookie与Session)

作用是共享数据,有 Request、Session、Application 三种。

Request     是一次请求

Session      是一个会话

Application 是多次会话(从Tomcat开启到关闭)

既然讲到了 Session,那么 Session 和 Cookie 有什么区别呢?

一 项新的技术诞生肯定是为了解决什么问题。做开发的都必须知道 Http 协议吧,Http的特征还记得吗?

(1)支持C/S模式;(2)简单快速;(3)灵活;(4)无连接;(5)无状态。

Cookie 和 Session 都是为了解决 Http 没有事务处理的记忆能力。

为了解决 Http 协议无法维持状态的问题,1994 年网景通讯的 一 名员工 Lou Montulli将 “Magic Cookies” 的概念应用到 Web 通讯中。他试图解决 Web 的第 一 个购物车应用,现在购物车成了购物网站的支柱。他的原始说明文档提供了 Cookie 工作原理的基本信息,该文档后来被作为规范纳入到 RFC 2109(大多数浏览器的实现参考文档)中,最终被纳入到 RFC 2965 中。Montulli 也被授予 Cookie 的美国专利。网景浏览器在它的第 一 个版本中就开始支持 Cookie,现在所有 Web 浏览器都支持 Cookie,但是有浏览器禁用Cookie的情况,后面再说解决方案。

Cookie

Cookie 是浏览器保存在用户电脑上的 一 小段文本,用来保存用户在网站上的必要的信息。Web 页面或服务器告诉浏览器按照 一 定的规范存储这些信息,并且在以后的所有请求中,这些信息就会自动加在 Http 请求头中发送给服务器,服务器根据这些信息判断不同的用户。并且 Cookie 本身是安全的。还可以后台加密之后,用setCookie发给浏览器。

Session

使用 Cookie 和附加URL参数都可以将上 一 次请求的状态信息传递到下 一 次请求中,但是如果传递的状态信息较多,将极大降低网络传输效率和增大服务器端程序处理的难度。所以,可将这些状态信息只保留在服务端的 Session 中即可。

Session 只存储在服务端中,使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。Session  不会在网络中传输,所以相对 Cookie 更安全。如果要把 Session 的Session ID(会话标识号)给客户端,只能通过 Cookie 来传递,如果浏览器禁用Cookie自然就不能得到Session了。

解决浏览器禁用Cookie的问题

解决方案:URL重写,保证在客户端禁用或不支持COOKIE时,仍然可以使用Session。

// 用于对sendRedirect方法后的url地址进行重写。

response. encodeRedirectURL(Java.lang.String url)

// 用于对表单action和超链接的url地址进行重写

response. encodeURL(java.lang.String url)

 

Java中增强一个类中的方法有几种方式?分别是?

/**
 * Java中增强一个类中的方法有几种方式?分别是?
 * 1、继承
 * 2、装饰者
 * 3、反射(动态代理)
 *
 * @author DJun
 * @date 2019/10/16
 */
public class StrengthenFunction {
    public static void main(String[] args) {
        // 继承
        new Action().action();
        // 装饰者
        new PigServiceImpl().selectAll();
        // 动态代理
        try {
            // 第一种创建 Class 实例的方法
            //Class clazz = Class.forName("interview.BaseAction");
            // 第二种
            Class clazz = BaseAction.class;
            BaseAction baseAction = (BaseAction) clazz.newInstance();

            System.out.println("---获取运行时类全部的方法,这里列出来只是为了复习---");
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                System.out.println("方法名:" + method.getName());
                Class<?>[] parameterTypes = method.getParameterTypes();
                for (Class<?> param : parameterTypes) {
                    String parameterName = param.getName();
                    System.out.println("参数名:" + parameterName);
                }
            }
            System.out.println("--------------------------------------");


            // 获取单个方法
            Method actionMethod = clazz.getMethod("action");
            actionMethod.invoke(baseAction);

        } catch (InstantiationException | IllegalAccessException |
                NoSuchMethodException | InvocationTargetException e) {
            e.printStackTrace();
        }

    }

    public void action(){

    }

}

//------------- 继承 ----------------

class Action extends BaseAction {
    @Override
    public void action() {
        System.out.println("长成28一斤的大猪了");
    }

}

class BaseAction {
    private Integer id;

    public void action() {
        System.out.println("小猪崽");
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "BaseAction{" +
                "id=" + id +
                '}';
    }
}
//---------------- 装饰者 --------------

interface PigService {
    void selectAll();
}

class PigServiceImpl implements PigService {
    @Override
    public void selectAll() {
        System.out.println("查询全部的猪");
    }
}
//------------- 二者对比 ------------------
/*

装饰者模式和继承的区别

继承实现的增强类:
  优点:代码结构清晰,简单易实现
  缺点:对需要增强的类需创建具体的子类来重写方法,这样会导致继承体系过于庞大。

装饰模式实现的增强类:
  优点:内部可以通过多态技术对多个需要增强的类进行增强
  缺点:需要内部通过多态技术维护需要增强的类的实例。进而使得代码稍微复杂。

 */

Mybatis和JDBC的区别

JDBC是Java提供的一个操作数据库的API; MyBatis是一个持久层ORM框架,底层是对JDBC的封装

MyBatis对JDBC操作数据库做了一系列的优化:
(1) mybatis使用已有的连接池管理,避免浪费资源,提高程序可靠性。
(2) mybatis提供插件自动生成DAO层代码,提高编码效率和准确性。
(3) mybatis 提供了一级和二级缓存,提高了程序性能。
(4) mybatis使用动态SQL语句,提高了SQL维护。(此优势是基于XML配置)
(5) mybatis对数据库操作结果进行自动映射

谈谈 synchronized 和 ReenTrantLock 的区别?

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 谈谈 synchronized 和 ReenTrantLock 的区别?
 *
 * ① 两者都是可重入锁
 * 两者都是可重入锁。“可重入锁”概念是:自己可以再次获取自己的内部锁。比如一个线程获得了某个对象的锁,此时
 * 这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果不可锁重入的话,就会造成死
 * 锁。同一个线程每次获取锁,锁的计数器都自增 1,所以要等到锁的计数器下降为 0 时才能释放锁。
 *
 * ② synchronized 依赖于 JVM 而 ReenTrantLock 依赖于 API
 * synchronized 是依赖于 JVM 实现的,前面我们也讲到了 虚拟机团队在 JDK1.6 为 synchronized 关键字进行了很多
 * 优化,但是这些优化都是在虚拟机层面实现的,并没有直接暴露给我们。ReenTrantLock 是 JDK 层面实现的(也就
 * 是 API 层面,需要 lock() 和 unlock 方法配合 try/finally 语句块来完成),所以我们可以通过查看它的源代码,来看
 * 它是如何实现的。
 *
 * ③ ReenTrantLock 比 synchronized 增加了一些高级功能
 * 相比 synchronized,ReenTrantLock 增加了一些高级功能。主要来说主要有三点:①等待可中断;②可实现公平锁;
 *
 * ③ 可实现选择性通知(锁可以绑定多个条件)
 *
 * ReenTrantLock 提供了一种能够中断等待锁的线程的机制,通过 lock.lockInterruptibly()来实现这个机制。也
 * 就是说正在等待的线程可以选择放弃等待,改为处理其他事情。
 * ReenTrantLock 可以指定是公平锁还是非公平锁。而 synchronized 只能是非公平锁。所谓的公平锁就是先等
 * 待的线程先获得锁。 ReenTrantLock 默认情况是非公平的,可以通过 ReenTrantLock 类的
 * ReentrantLock(boolean fair)构造方法来制定是否是公平的。
 * synchronized 关键字与 wait()和 notify/notifyAll()方法相结合可以实现等待/通知机制,ReentrantLock 类当然也
 * 可以实现,但是需要借助于 Condition 接口与 newCondition() 方法。Condition 是 JDK1.5 之后才有的,它具有很
 * 好的灵活性,比如可以实现多路通知功能也就是在一个 Lock 对象中可以创建多个 Condition 实例(即对象监视
 * 器),线程对象可以注册在指定的 Condition 中,从而可以有选择性的进行线程通知,在调度线程上更加灵
 * 活。 在使用 notify/notifyAll()方法进行通知时,被通知的线程是由 JVM 选择的,用 ReentrantLock 类结合
 * Condition 实例可以实现“选择性通知” ,这个功能非常重要,而且是 Condition 接口默认提供的。而
 * synchronized 关键字就相当于整个 Lock 对象中只有一个 Condition 实例,所有的线程都注册在它一个身上。如果
 * 执行 notifyAll()方法的话就会通知所有处于等待状态的线程这样会造成很大的效率问题,而 Condition 实例的
 * signalAll()方法 只会唤醒注册在该 Condition 实例中的所有等待线程。
 *
 * ④ 性能已不是选择标准
 *
 * @author DJun
 * @date 2019/10/6
 */
public class ReenTrantLockAndSynchronized {

    private static Lock lock1 = new ReentrantLock();
    private static Lock lock2 = new ReentrantLock();

    public static void main(String[] args)
            throws InterruptedException {

        //该线程先获取锁1,再获取锁2
        Thread thread = new Thread(new ThreadDemo(lock1, lock2));
        //该线程先获取锁2,再获取锁1
        Thread thread1 = new Thread(new ThreadDemo(lock2, lock1));

        thread.setName("线程1");
        thread1.setName("线程2");

        thread.start();
        thread1.start();
    }

   private static class ThreadDemo implements Runnable {
        private Lock firstLock;
        private Lock secondLock;

        public ThreadDemo(Lock firstLock, Lock secondLock) {
            this.firstLock = firstLock;
            this.secondLock = secondLock;
        }

        @Override
        public void run() {
            try {

                while (!lock1.tryLock()) {
                    TimeUnit.MILLISECONDS.sleep(10);
                }

                while (!lock2.tryLock()) {
                    lock1.unlock();
                    TimeUnit.MILLISECONDS.sleep(10);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                firstLock.unlock();
                secondLock.unlock();
                System.out.println(Thread.currentThread().getName() + "正常结束!");
            }
        }
    }
}

用 SQL 完成如图要求的数据展示

现在很少公司还在自己手动写SQL了,但是对于基础和 SQL优化来说成了必问的一个问题了,本题主要考查对SQL的掌握程度。

SELECT pref_nare 城区,
MAX(CASE sex WHEN 1 THEN population ELSE 0 END) AS '男',
MAX(CASE sex WHEN 0 THEN population ELSE 0 END) AS '女',
#也可使用IF函数实现
SUM(`population`) 总人口数
FROM `region_population`
GROUP BY pref_name
SELECT
IF(pref_name='南京','其它','杭州') AS region,
# 也可用CASE函数实现
SUM(population) 
FROM `region_population` 
GROUP BY region 

java中,3*0.1与0.3是否相等?4*0.1与0.4是否相等?

这里的数值相乘是十进制的,这些小数的分母部分如果能够被10(进制数)整除,那么就能精确表示,如1/2、1/4、1/5、1/8和 1/10。但在计算机中是以二进制来计算的,因此只能精确表示分母被2(进制数)整除,计算机计算完成之后又转成十进制。原因是什么?这个问题的原因是很复杂的,既跟浮点数的表示有关,也跟Java的设计机制有关。

 

== 和 equals 的区别是什么?

== 基本类型比较的是值,引用类型比较的是地址。

equals 是超类Object中的方法,用来比较两个变量指向的内存地址是否相同。

 

final 在 java 中有什么作用?

修饰变量:被修饰的变量只能赋一次值,不可改变。修饰成员变量的话,必须在 声明时 / 构造方法 / 静态代码块 中对它赋值。

修饰方法:修饰的方法不能被重写,但是可以重载!

修饰类(抽象类除外,抽象类中有抽象方法,就是让子类继承实现的):让子类不能继承。

 

String str="i" 与 String str=new String(“i”)一样吗?

String str = "i" 直接赋值,JVM首先会在常量池中是否存在 i 这个对象,不存在的话就创建一个,然后返回引用地址给str。

使用 new 的话,无论常量池中或者堆中是否存在该字符串,都会在堆中创建一个字符串对象,然后将堆中的这个对象的地址返回赋给引用str。

 

java 中的 Math.round(-1.5) 等于多少?

Math.round()四舍五入的原理,小数:

等于0.5,取原数字+0.5。

大于0.5,舍去小数,绝对值+1。

小于0.5,仅舍去小数。

public class testRound {
    public static void main(String[] args){
         System.out.println(Math.round(0.5));  //1
        System.out.println(Math.round(1.4));   //1
        System.out.println(Math.round(1.5));   //2
        System.out.println(Math.round(1.6));   //2
        System.out.println(Math.round(1.7));   //2
        System.out.println(Math.round(-1.4));  //-1
        System.out.println(Math.round(-1.5));  //-1
        System.out.println(Math.round(-1.6));  //-2
    }
}

 

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值