随笔(各种知识点)

1.逻辑与和短路与的区别

  • &是逻辑与运算会执行左右两边的判断条件,如果都为true则返回true,否则返回false
    &&是短路与,会先执行左边的判断,如果为真则执行右边的判断,否则直接返回false
    |逻辑或,会执行左右两边的逻辑判断,如果都为false则返回false,否则返回true
    ||是短路或,会先执行左边的逻辑判断,如果是true则直接返回true,否则执行右边逻辑判断并根据右边结果返回

2.基本数据类型

  • char boolean long short double float byte int

3.控制台输入程序

  • Scanner sc = new Scanner(System.in);
    String inputStr = sc.next();
    System.out.println(inputStr);

4.单精度和双精度浮点数的区分

  • //小数如果不强制说明则按照双精度浮点型处理
    //默认小数为双精度浮点型,需要转换或者后面添加f表示对应的单精度
    //单精度和双精度之间的区别在于一个是32位一个是64位,后者比前者的表示范围要精确
    float f = 3.4f;
    float ff = (float)3.4;

5.short 中+和+=的区别

  • //第一处报错是因为f1+1执行完成之后是int型,需要强制转换成short
    short f1 = 1;
    f1 = f1 + 1;//(报错)
    //此处没有报错是因为下面的语句相当于f1 = (short)(f1+1);
    f1 +=1;

6 ==和equals()以及hashcode

  • // ==比较的是两个引用指向的内存地址,equals()在object中使用的也是==符号
    // 但是在String和Integer中都对equals()进行了重写,从而使得内存中对应的内容相同即可,与内存地址无关
    // hashCode是jdk根据对象的地址或者字符串或者数字算出来的int类型的数值,(1)如果两个对象相同(equals方法返回true),那么它们的hashCode值一定相同;(2)如果两个对象的hashCode相同,它们并不一定是同一个对象(如Integer)。
    // 但是String和Integer对hashcode()也进行了重写,只要值相同返回的hashcode也是相同的

    System.out.println("Integer test");
    Integer n1 = new Integer(155);
    Integer n2 = new Integer(155);
    System.out.println("n1 == n2:" + (n1 == n2));
    System.out.println("n1.equal(n2) : " + (n1.equals(n2)));
    System.out.println("n hashcode: " + n1.hashCode() + "   "
            + n2.hashCode());
    System.out.println("Object test");
    Test123 testObject1 = new Test123();
    Test123 testObject2 = new Test123();
    Test123 testObject3 = testObject1;
    System.out.println(testObject1.equals(testObject2));
    System.out.println(testObject1 == testObject2);
    System.out.println(testObject1 == testObject3);
    System.out
            .println("Test123 hashcode : " + testObject1.hashCode() + "  "
                    + testObject2.hashCode() + "  "
                    + testObject3.hashCode());
    
    System.out.println("String test");
    String str1 = new String("str");
    String str2 = new String("str");
    System.out.println(str1 == str2);
    System.out.println(str1.equals(str2));
    System.out.println("str hashcode : " + str1.hashCode() + "   " + str2.hashCode());
    
  • 输出结果如下:
    Integer test
    n1 == n2:false
    n1.equal(n2) : true
    n hashcode: 155 155
    Object test
    false
    false
    true
    Test123 hashcode : 2045891656 1940390275 2045891656
    String test
    false
    true
    str hashcode : 114225 114225

7.Integer自动装箱的一个误区

  • //自动装箱操作是调用valueOf(int i)的方法,如果数字在-128到127之间则会返回存储在常量池中的对应引用地址,否则才会新建常量对象,并返回新建的引用地址。
    Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150;
    Integer f5 = new Integer(100), f6 = new Integer(100);
    System.out.println(f1 == f2);
    System.out.println(f3 == f4);
    System.out.println(f5 == f6);
  • 输出结果为:true false fasle

8.内存中的栈(stack)、堆(heap)和方法区(method area)

  • 栈中一般存储:基本数据类型变量,对象的引用以及函数调用的现场保存。
  • 堆中存储的是:通过new关键字和构造函数创建的对象。
  • 方法区存储的是:常量、静态变量、JIT编译器编译后的代码等数据。常量池属于方法区。方法区是堆中的一个逻辑分区。
  • 例如:String str = new String(“hello”); 语句中变量str引用放在栈上,用new创建出来的字符串对象放在堆上,而”hello”这个字面量是放在方法区中的常量池。

9.String 中intern()方法的作用(理解偏差带澄清!!!!):

  • 返回常量池中的地址引用,从而节约内存。
  • 在jdk1.6及一下版本:判断常量池中是否存在对应的字符串,如果存在则返回此字符串在常量池中的引用地址,如果不存在则在常量池中创建字符串并返回引用地址。
  • 在jdk1.7及以上版本:判断常量池中是否存在对应的字符串,如果存在则返回此字符串在常量池中的引用地址,如果不存在则将常量池中所有关于此字符串的引用都指向自己的引用地址。
  • 例如1:
  • String str1 = new String(“SEU”) + new String(“Calvin”);
    System.out.println(str1.intern() == str1);
    System.out.println(str1 == “SEUCalvin”);
    结果: true true
    因为:str1.intern() == str1就是上面例子中的情况,str1.intern()发现常量池中不存在“SEUCalvin”,因此指向了str1。 “SEUCalvin”在常量池中创建时,也就直接指向了str1了。两个都返回true就理所当然啦。
  • 例如2:
  • String s1 = new StringBuilder(“go”).append(“od”).toString();
    System.out.println(s1.intern() == s1);
    String s2 = new StringBuilder(“ja”).append(“va”).toString();
    System.out.println(s2.intern() == s2);
    结果: jdk1.6: false fasle jdk1.7: true false
    因为:首先”java”应该已经存储在常量区中,1.6中intern()获取的是常量池中的引用,但是具体的s1/s2指的是堆中的地址。在1.7中第一个因为常量池中没有“good”所以直接指向自己地址的引用,但是第二个因为已经存在所以返回的是常量池中的地址引用。

10.利用Math取整

  • Math.floor();向下取整
  • Math.ceil();向上取整
  • Math.round();四舍五入取整(数字+0.5,向下取整)

11.switch支持的类型

  • byte、short、char、int、enum、String

12.如何计算负数的二进制

  • 例如:-5(以8位byte为例)
    获取8位5的二进制 (原码) 00000101
    对其取反(反码) 11111010
    加一(补码)11111011

13.java中移位运算符

  • 左移运算符(<<)规则:丢弃最高位(符号位同样丢弃),0补最低位
    例如:快速计算2*8 (2<<3)

  • 带符号右移运算符(>>)规则:如果要移走的值为负数,每一次右移都在左边补1,如果要移走的值为正数,每一次右移都在左边补0。

  • 无符号位右移(>>>)规则:忽略了符号位扩展,0补最高位。

14.java中是值传递还是引用传递?

  • 是值传递。Java语言的方法调用只支持参数的值传递。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以在被调用过程中被改变,但对对象引用的改变是不会影响到调用者的。

15.String和StringBuilder、StringBuffer的区别

  • String是只读字符串,每次在对字符串进行操作的时候相当于新创建了一个字符串,因此如果字符串修改过多的话不要使用String。其底层+也是使用StringBuilder进行实现。
  • StringBuffer是可变字符串,因为内部方法被synchronized修饰,所以是线程安全的。
  • StringBuilder是可变字符串,线程是不安全的,但效率相对比较高。
  • 总结:如果使用少量的字符串操作,使用 (+运算符)连接字符串;
    如果频繁的对大量字符串进行操作,则使用
    1:全局变量或者需要多线程支持则使用StringBuffer;
    2:局部变量或者单线程不涉及线程安全则使有StringBuilder。
String s1 = "Programming";
String s5 = "Program" + "ming";
System.out.println(s1 == s5);   //true  后者在编译期,和前者一样都指向常量池中的“Programming”,地址相同(这是java的特殊处理)。

16.为什么不能根据返回类型来区分重载

  • 对于只关心调用过程,不关心返回值的程序,如果允许根据返回类型重载,那么编译器就会不清楚自己应该调用那个方法。

17.类加载器(待补充)

  • 它负责在运行时查找和装入类文件的类。
  • 类的加载是指把类的.class文件中的数据读入到内存中,通常是创建一个字节数组读入.class文件,然后产生与所加载类对应的Class对象,当类被加载后就进入连接阶段,这一阶段包括验证、准备(为静态变量分配内存并设置默认的初始值)和解析(将符号引用替换为直接引用)三个步骤。最后JVM对类进行初始化,包括:1)如果类存在直接的父类并且这个类还没有被初始化,那么就先初始化父类;2)如果类中存在初始化语句,就依次执行这些初始化语句。
  • 类加载器包括:根加载器(BootStrap)、扩展加载器(Extension)、系统加载器(System)和用户自定义类加载器(java.lang.ClassLoader的子类)
  • Bootstrap:一般用本地代码实现,负责加载JVM基础核心类库(rt.jar);
    Extension:从java.ext.dirs系统属性所指定的目录中加载类库,它的父加载器是Bootstrap;
    System:又叫应用类加载器,其父类是Extension。它是应用最广泛的类加载器。它从环境变量classpath或者系统属性java.class.path所指定的目录中记载类,是用户自定义加载器的默认父加载器。

18.类型占用字节数

  • byte 1个字节
  • short 2个字节
  • int 4个字节
  • long 8个字节
  • float 4个字节
  • double 8个字节
  • 一个汉字 占用2个字节

19.抽象类(abstract class)和接口(interface)有什么异同

相同点:

  • 两者都不能被实例化,但是都可以创建对象类型的引用。
  • 一个类如果继承了某个抽象类或者实现了某个接口,都需要对抽象的方法进行实现,否则该类必须声明为抽象类。

不同点:

  • 抽象类中的方法是可以有具体实现的,但是接口中的所有方法都是没有实现的。
  • 抽象类是有构造器的,但是接口中没有。
  • 抽象方法的成员方法可以声明为:public/private/默认/protected,但是接口中的成员方法必须声明为:public。
  • 抽象类中的成员变量是有各种形态的,但是接口中的成员变量其实是常量(ps:在枚举还没有出现之前,使用接口来做常量的保存)。

ps:有抽象方法一定是抽象类,但是抽象类不一定有抽象方法。

20.抽象的(abstract)方法是否可同时是静态的(static),是否可同时是本地方法(native),是否可同时被synchronized修饰,可否使用private修饰

  • 都不能。
  • 抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。
  • 本地方法是由本地代码(如C代码)实现的方法,而抽象方法是没有实现的,也是矛盾的。
  • synchronized和方法的实现细节有关,抽象方法不涉及实现细节,因此也是相互矛盾的。
  • private修饰的方法不能够被继承,但是抽象方法是需要通过继承来实现的,因此也是互相矛盾的。

21.在代码finlly块中,分别修改String和Map对象,为什么String的值没有修改?但是Map对象的值被修改了?

代码如下:

package com.anran.clone;

import java.util.HashMap;
import java.util.Map;

/**
 * @author anran
 * @version 创建时间:2017年9月5日 下午8:11:58 类说明 :
 */
public class CloseClass implements Cloneable {
    public static void main(String[] args) {
         System.out.println(dd().get("a"));
         System.out.println(cc());
    }

    @SuppressWarnings("finally")
    public static String cc(){
        String str = "cc";
        try{
            return str;
        }finally{
            str = str + "b";
        }
    }

    public static Map dd() {
        Map<String, String> map = new HashMap<String, String>();
        try {
            map.put("a", "a");
            return map;
        } finally {
            map.put("a", "b");
        }

    }
}

输出:

b
cc

分析原因:

  • finally是在方法返回之后执行的操作。(即在try的return之后,但是在返回调用者之前)。
  • 为什么Map中的内容被修改,但是String中的内容没有被修改过来呢?首先java是值传递,两者都是返回了对应堆中的值。对于String对象进行操作的时候相当于重新再堆中创建了一个对象,但是返回的仍然是return时候的那个对象。但是对于Map对象而言,修改的是Map中的值,返回的仍然是堆中的同一个对象,因此发生了改变。

22.克隆(clone)

(1).实现Cloneable接口,重写Object类中的clone()方法。是浅层次的克隆,如果类中有依赖于其它类,那么只会克隆其它类的引用,而不是新建其它类。

代码:

package com.anran.clone;

/**
 * @author anran
 * @version 创建时间:2017年9月7日 上午8:42:50 类说明 :
 */
public class User {

    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "user name is " + name + ", user age is " + age;
    }
}

package com.anran.clone;

/**
 * @author anran
 * @version 创建时间:2017年9月5日 下午8:11:58 类说明 :
 */
public class Account implements Cloneable {

    private User user;
    private int balance;

    Account(User user, int balance) {
        this.user = user;
        this.balance = balance;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public int getBalance() {
        return balance;
    }

    public void setBalance(int balance) {
        this.balance = balance;
    }

    public Account clone() throws CloneNotSupportedException {
        return (Account) super.clone();
    }

    public String toString() {
        return user.toString() + ", balance is " + balance;
    }
}
package com.anran.clone;

import java.io.IOException;

/**
 1. @author anran
 2. @version 创建时间:2017年9月7日 上午8:49:39 类说明 :
 */
public class CloneTest {

    public static void main(String[] args) throws CloneNotSupportedException {
        System.out.println("*****start test Cloneable*******");
        User user = new User("anran", 25);
        Account account = new Account(user, 1000);
        System.out.println("account start info : " + account);
        Account copyAccount = account.clone();
        // 修改clone之后的对象
        copyAccount.setBalance(8000);
        User copyUser = copyAccount.getUser();
        copyUser.setAge(20);
        copyUser.setName("xishan");
        System.out.println("account end   info : " + account);
    }
}

输出:

*****start test Cloneable*******
account start info : user name is anran, user age is 25, balance is 1000
account end   info : user name is xishan, user age is 20, balance is 1000

解释:在测试代码中,Account中的User对象只是复制了一个引用到新的对象中,在对新的对象中的User进行操作时,同时也修改了原有对象中User的值。

(2).实现Serializable接口,是可以进行深度克隆的。要求克隆类中的依赖类也同样需要实现Serializable接口。

代码:

package com.anran.clone;

import java.io.Serializable;

/**
 * @author anran
 * @version 创建时间:2017年9月7日 上午9:52:51 类说明 :
 */
public class UserTwo implements Serializable {

    private static final long serialVersionUID = 4859165981038518307L;
    private String name;
    private int age;

    public UserTwo(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "user name is " + name + ", user age is " + age;
    }

}

package com.anran.clone;

import java.io.Serializable;

/**
 * @author anran
 * @version 创建时间:2017年9月7日 上午9:40:26 类说明 :
 */
public class AccountTwo implements Serializable {

    private static final long serialVersionUID = 1L;
    private UserTwo user;
    private int balance;

    AccountTwo(UserTwo user, int balance) {
        this.user = user;
        this.balance = balance;
    }


    public UserTwo getUser() {
        return user;
    }

    public void setUser(UserTwo user) {
        this.user = user;
    }

    public int getBalance() {
        return balance;
    }

    public void setBalance(int balance) {
        this.balance = balance;
    }

    public String toString() {
        return user.toString() + ", balance is " + balance;
    }

}

package com.anran.clone;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * @author anran
 * @version 创建时间:2017年9月7日 上午9:15:34 类说明 :
 */
public class CloneUtil {

    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T clone(T obj) throws IOException,
            ClassNotFoundException {
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bout);
        oos.writeObject(obj);

        ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bin);
        return (T) ois.readObject();
    }

}


package com.anran.clone;

import java.io.IOException;

/**
 * @author anran
 * @version 创建时间:2017年9月7日 上午8:49:39 类说明 :
 */
public class CloneTest {

    public static void main(String[] args) throws ClassNotFoundException, IOException  {
        System.out.println("*****start test Serializable*********");
        UserTwo userTwo = new UserTwo("anran", 25);
        AccountTwo accountTwo = new AccountTwo(userTwo, 1000);
        System.out.println("accountTwo start info : " + accountTwo);
        // 复制
        AccountTwo copyAccountTwo = CloneUtil.clone(accountTwo);
        copyAccountTwo.setBalance(800);
        UserTwo copyUserTwo = copyAccountTwo.getUser();
        copyUserTwo.setAge(20);
        copyUserTwo.setName("xishan");
        System.out.println("accountTwo end   info : " + accountTwo);

    }

}

输出:
*****start test Serializable*********
accountTwo start info : user name is anran, user age is 25, balance is 1000
accountTwo end info : user name is anran, user age is 25, balance is 1000

解释:在对进行克隆之后的对象进行操作之后,原始对象的信息没有受到影响,说明进行了深度克隆。

23.serialVersionUID的作用

1.首先理解一下序列化和反序列化(序列化: 将数据结构或对象转换成二进制串的过程。反序列化:将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程。)。序列化是为了能够将信息进行长时间的保存。
2. serialVersionUID是反序列化时的验证条件,当序列化之前和序列化之后的serialVersionUID不相同时,会反序列化失败。
3. serialVersionUID的由来。当一个类实现了Serializable接口的时候,系统会默认根据类的各个方面(属性,方法。。。)得到一个serialVersionUID,当类中内容被修改之后serialVersionUID也会被修改。
4.那么为什么需要手动声明一个serialVersionUID呢?那是为了当一个类中的内容有所改动之后,在改动之前序列化的信息能够正常反序列化。

例如:

原码:

package com.anran.clone;

import java.io.Serializable;

/**
 * @author anran
 * @version 创建时间:2017年9月7日 上午10:59:39 类说明 :
 */
public class Person implements Serializable {

    private String name;
    private int age;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

package com.anran.clone;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
 * @author anran
 * @version 创建时间:2017年9月7日 上午11:00:21 类说明 :
 */
public class WhySerialversionUID {

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

        // 这里是把对象序列化到文件
        Person crab = new Person();
        crab.setName("Mr.Crab");

        ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(
                "crab_file"));
        oo.writeObject(crab);
        oo.close();

        // 这里是把文件中的内容进行反序列化,我们先注释掉,一会儿用
        // ObjectInputStream oi = new ObjectInputStream
        // (new FileInputStream("crab_file"));
        // Person crab_back = (Person) oi.readObject();
        // System.out.println("Hi, My name is " + crab_back.getName());
        // oi.close();

    }

}

解释:main方法中,没有注释掉的部分时将Person对象进行序列化保存到文件中,注释掉的内容是将文件中的内容进行反序列化,从而获取到Person对象。

开始测试的时候我们不修改Person中的代码,序列化和反序列化能够正常进行。但是当我们对Person中内容进行改动的时候(如:添加一个字段),如果此时将内存之前已经序列化好的对象进行反序列化,则会出现一下异常:
( java.io.InvalidClassException: com.anran.clone.Person; local class incompatible: stream classdesc serialVersionUID = -5451716667017994954, local class serialVersionUID = 7998124161639063224

这个异常意思是说两者的serialVersionUID不同,不能够进行反序列化。

但是如果我们手动在类中添加了serialVersionUID,此时上述问题就不存在。

24.垃圾回收(GC)

  1. java程序员如何调用GC? System.gc() 或Runtime.getRuntime().gc().
  2. GC的特点:线程优先级比较低,以及不可遇见性(不知道什么时候被调用)。
  3. 垃圾回收的工作方式:
    (1)停止-复制(停止其它程序的运行,将存活的对象从当前堆复制到另外一个堆中,清理新堆以外的,特点是内存是连续的)。
    (2)标记-清除(遍历所有引用,并标记所有的存活对象,当标记工作完成之后,释放那些没有标记的对象,特点是内存不连续)。
  4. 垃圾回收相关的JVM参数:
    -Xms / -Xmx — 堆的初始大小 / 堆的最大大小
    -Xmn — 堆中年轻代的大小
    -XX:-DisableExplicitGC — 让System.gc()不产生任何作用
    -XX:+PrintGCDetails — 打印GC的细节
    -XX:+PrintGCDateStamps — 打印GC操作的时间戳
    -XX:NewSize / XX:MaxNewSize — 设置新生代大小/新生代最大大小
    -XX:NewRatio — 可以设置老生代和新生代的比例
    -XX:PrintTenuringDistribution — 设置每次新生代GC后输出幸存者乐园中对象年龄的分布
    -XX:InitialTenuringThreshold / -XX:MaxTenuringThreshold:设置老年代阀值的初始值和最大值
    -XX:TargetSurvivorRatio:设置幸存区的目标使用率

25.为什么计算器日期都是1970 年 1 月 1 日 00:00:00

  • 因为起初计算机是32位,最大的整数是2^31-1(ps:因为有符号位,所以占用了),然后根据一年有365*24*60*60秒,算出最多表示68年,然后根据计算机出现的年份以及兼顾后面的时间,设置1970 年 1 月 1 日 00:00:00为起始时间。后面64为出现之后,就没有时间不够用了这一说。

26.jdk1.7时间格式化的相关操作

原码:

package com.anran.time;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author anran
 * @version 创建时间:2017年9月7日 下午3:31:12 类说明 :
 */
public class TimeTest {

    public static void main(String[] args) throws ParseException {
        SimpleDateFormat format = new SimpleDateFormat(
                "yyyy-MM-dd HH:mm:ss:SSS");
        Date date = format.parse("2017-09-07 17:34:49:142");
        System.out.println(format.format(date));
        SimpleDateFormat myFmt = new SimpleDateFormat(
                "一年中的第 D 天   一年中第w个星期     一月中第W个星期   z时区");
        System.out.println(myFmt.format(date));
        // 获取据自1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数
        System.out.println(date.getTime());

    }

}

输出:

2017-09-07 17:34:49:142
一年中的第 250 天   一年中第36个星期     一月中第2个星期   CST时区
1504776889142

26.jdk1.8时间相关操作

原码:

package com.anran.time;

import java.time.Clock;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

/**
 * @author anran
 * @version 创建时间:2017年9月7日 下午5:51:11 类说明 :
 */
public class TimeTset18 {

    public static void main(String[] args) {
        // 1.8中获取当前时间(包含时间+日期)
        LocalDateTime localDateTime = LocalDateTime.now();
        System.out.println(localDateTime);
        // 1.8中格式化对应时间
        DateTimeFormatter newFormatter = DateTimeFormatter
                .ofPattern("yyyy-MM-dd HH:mm:ss:SSS");
        System.out.println(localDateTime.format(newFormatter));
        // 获取单个时间
        System.out.println(localDateTime.getYear() + "-"
                + localDateTime.getMonth() + "-"
                + localDateTime.getDayOfMonth() + "("
                + localDateTime.getMonthValue() + ") "
                + localDateTime.getHour() + ":" + localDateTime.getMinute()
                + ":" + localDateTime.getSecond() + ":"
                + ((localDateTime.getNano() / 1000) / 1000));
        // 1.8中获取当前时间(只包含日期)
        LocalDate localDate = LocalDate.now();
        System.out.println(localDate);
        // 1.8中获取当前时间(只包含时间)
        LocalTime localTime = LocalTime.now();
        System.out.println(localTime);

        // 1.7获取当前据时间起点的毫秒数
        System.out.println(System.currentTimeMillis());
        // 1.8获取当前据时间起点的毫秒数
        System.out.println(Clock.systemDefaultZone().millis());
    }

}

输出:

2017-09-07T18:53:01.505
2017-09-07 18:53:01:505
2017-SEPTEMBER-7(9) 18:53:1:505
2017-09-07
18:53:01.523
1504781581524
1504781581524

27.操作时间方式

原码:

package com.anran.time;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
 * @author anran
 * @version 创建时间:2017年9月7日 下午3:31:12 类说明 :
 */
public class TimeTest {

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

        Calendar calendar = Calendar.getInstance();
        // 获取自19701100:00:00 GMT到对应时间的毫秒数
        System.out.println(calendar.getTimeInMillis());
        // 获取当前的年月日时分秒毫秒
        System.out.println(calendar.get(Calendar.YEAR)
                + "-"
                + calendar.get(Calendar.MONTH)
                + "-" // 0-11
                + calendar.get(Calendar.DATE) + " "
                + calendar.get(Calendar.HOUR_OF_DAY) + ":"
                + calendar.get(Calendar.MINUTE) + ":"
                + calendar.get(Calendar.SECOND) + ":"
                + calendar.get(Calendar.MILLISECOND));
        System.out.println("星期:"
                + calendar.get(Calendar.DAY_OF_WEEK) // 周日是1
                + "  今年的第" + calendar.get(Calendar.DAY_OF_YEAR) + "天"
                + "  本月的第" + calendar.get(Calendar.DAY_OF_MONTH) + "天"
                + "  本年的第" + calendar.get(Calendar.WEEK_OF_YEAR) + "周"
                + "  本月的第" + calendar.get(Calendar.WEEK_OF_MONTH) + "周");
        // 获取制定格式的日期
        SimpleDateFormat dataFormat = new SimpleDateFormat(
                "yyyy-MM-dd HH:mm:ss:SSS");
        System.out.println(dataFormat.format(calendar.getTime()));

        // 对应24小时加10小时(自动到下一天)
        calendar.add(Calendar.HOUR_OF_DAY, 10);
        System.out.println(dataFormat.format(calendar.getTime()));
        // 对应24小时减10小时(自动回到当天)
        calendar.add(Calendar.HOUR_OF_DAY, -10);
        System.out.println(dataFormat.format(calendar.getTime()));
        //获取当前月的最后一天
        System.out.println(calendar.getActualMaximum(Calendar.DAY_OF_MONTH));

        // Calendar对象重新设置时间
        Calendar newCalendar = Calendar.getInstance();
        newCalendar.setTime(new Date());
        // 获取两个时间之间相差的天数
        long dayCount = (newCalendar.getTimeInMillis() - calendar
                .getTimeInMillis()) / (24 * 60 * 60 * 1000);

    }
}

输出:

1504777367284
2017-8-7 17:42:47:284
星期:5  今年的第250天  本月的第7天  本年的第36周  本月的第2周
2017-09-07 17:42:47:284
2017-09-08 03:42:47:284
30

28.Java和JavaSciprt

  1. java:面向对象、需要编译、强类型变量语言。
  2. javaScript:面向函数、解释性语言、弱类型变量语言

29.Error和Exception有什么区别

  1. Error表示系统级的错误和程序不必处理的异常,如内存溢出,程序也没有办法作出响应。
  2. Exception表示需要捕捉或者需要程序进行处理的异常,是一种设计或实现问题,需要对应异常作出相应的处理。

30.运行时异常与受检异常

  1. 受检异常:受检异常跟程序运行的上下文环境有关,即使程序设计无误,仍然可能因使用的问题而引发。受检查异常(checked exception)都是编译器在编译时进行校验的,通过throws语句或者try{}cathch{} 语句块来处理检测异常。(常见的受检异常:ClassNotFountException/CloneNotSupportedException/NoSuchFieldException/NoSuchMetodException/IllegalAccessException)
  2. 运行时异常:运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误,只要程序设计得没有问题通常就不会发生。(常见的运行时异常有:NullPointException/ClassCastException/FileNotFountException/IOException/ArrayIndexOutOfBoundsException)

31.阐述final、finally、finalize的区别

  1. final:修饰符(关键字)有三种用法:如果一个类被声明为final,意味着它不能再派生出新的子类,即不能被继承,因此它和abstract是反义词。将变量声明为final,可以保证它们在使用中不被改变,被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取不可修改。被声明为final的方法也同样只能使用,不能在子类中被重写。
  2. finally:通常放在try…catch…的后面构造总是执行代码块,这就意味着程序无论正常执行还是发生异常,这里的代码只要JVM不关闭都能执行,可以将释放外部资源的代码写在finally块中。
  3. finalize:Object类中定义的方法,Java中允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在销毁对象时调用的,通过重写finalize()方法可以整理系统资源或者执行其他清理工作。

32.对于list/set/map的排序操作

原码:

package com.anran.collection;

import java.util.Random;

/**
 * @author anran
 * @version 创建时间:2017年9月8日 上午10:32:41 类说明 :
 */
public class Student {

    private static String[] names = { "A", "B", "C", "D", "E", "F", "G", "H",
            "I", "J" };
    private static int[] ages = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    private String name;
    private int age;
    private Random random = new Random();

    Student() {
        this.age = ages[random.nextInt(10)];
        this.name = names[random.nextInt(10)];
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "(name is " + this.name + ", age is " + this.age + ")";
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Student();
        }
    }

}


package com.anran.collection;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

/**
 * @author anran
 * @version 创建时间:2017年9月8日 上午8:32:16 类说明 :
 */
public class Sort {

    public static void main(String[] args) {
        sortList();
        sortSet();
        sortMap();
        useTreeSet();
        useMapTree();
    }

    public static void sortList() {
        System.out.println("*********start list**********");
        List<Student> list = new ArrayList<Student>();
        for (int i = 0; i < 10; i++) {
            list.add(new Student());
        }
        System.out.println(list);
        Collections.sort(list, new Comparator<Student>() {
            @Override
            // 表示需要重写的方法
            public int compare(Student o1, Student o2) {
                return o1.getAge() - o2.getAge();
            }
        });
        System.out.println(list);
        // 反转顺序
        Collections.reverse(list);
        System.out.println(list);
    }

    public static void sortSet() {
        System.out.println("**********start set **********");
        Set<Student> set = new HashSet<Student>();
        for (int i = 0; i < 10; i++) {
            set.add(new Student());
        }
        System.out.println(set);
        List<Object> list = new ArrayList<Object>();
        Collections.addAll(list, set.toArray());
        Collections.sort(list, new Comparator<Object>() {
            @Override
            public int compare(Object o1, Object o2) {
                Student s1 = (Student) o1;
                Student s2 = (Student) o2;
                return s1.getName().compareTo(s2.getName());
            }
        });
        System.out.println(list);
    }

    public static void sortMap() {
        System.out.println("********** start map");
        Map<String, Student> map = new HashMap<String, Student>();
        for (int i = 0; i < 10; i++) {
            Student student = new Student();
            map.put(student.getName(), student);
        }
        System.out.println(map);
        List<Map.Entry<String, Student>> list = new ArrayList<Map.Entry<String, Student>>(
                map.entrySet());
        Collections.sort(list, new Comparator<Map.Entry<String, Student>>() {
            @Override
            public int compare(Entry<String, Student> o1,
                    Entry<String, Student> o2) {
                return o1.getValue().getName()
                        .compareTo(o2.getValue().getName());
            }
        });
        System.out.println(list);
    }

    public static void useTreeSet() {
        System.out.println("*********start TreeSet");
        Set<Student> set = new TreeSet<Student>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                if (o1.equals(o2)) {
                    // 如果是同一对象,就不加入set,返回0
                    return 0;
                }
                // 如果第一个对象的相关属性比第二个大,则返回交换(1),否则不交换(-1)
                if (o1.getName().compareTo(o2.getName()) >= 0) {
                    return 1;
                }
                return -1;
            }
        });
        for (int i = 0; i < 10; i++) {
            set.add(new Student());
        }
        System.out.println(set);
    }

    public static void useMapTree() {
        System.out.println("***********start MapTree **********");
        // 默认按照键进行升序排序,不能按照map中的值进行排序(如果需要按照值进行排序,需要使用上面堆hashmap的方法)
        Map<String, Student> map = new TreeMap<String, Student>(
                new Comparator<String>() {
                    @Override
                    public int compare(String o1, String o2) {
                        // 降序
                        return o2.compareTo(o1);
                    }
                });
        for (int i = 0; i < 10; i++) {
            Student s = new Student();
            map.put(s.getName(), s);
        }
        System.out.println(map);
    }

}

输出:

*********start list**********
[(name is D, age is 7), (name is C, age is 10), (name is G, age is 3), (name is H, age is 8), (name is J, age is 7), (name is E, age is 2), (name is F, age is 3), (name is H, age is 7), (name is C, age is 7), (name is E, age is 4)]
[(name is E, age is 2), (name is G, age is 3), (name is F, age is 3), (name is E, age is 4), (name is D, age is 7), (name is J, age is 7), (name is H, age is 7), (name is C, age is 7), (name is H, age is 8), (name is C, age is 10)]
[(name is C, age is 10), (name is H, age is 8), (name is C, age is 7), (name is H, age is 7), (name is J, age is 7), (name is D, age is 7), (name is E, age is 4), (name is F, age is 3), (name is G, age is 3), (name is E, age is 2)]
**********start set **********
[(name is I, age is 3), (name is B, age is 9), (name is J, age is 3), (name is D, age is 10), (name is A, age is 10), (name is B, age is 10), (name is J, age is 3), (name is C, age is 1), (name is I, age is 5), (name is J, age is 1)]
[(name is A, age is 10), (name is B, age is 9), (name is B, age is 10), (name is C, age is 1), (name is D, age is 10), (name is I, age is 3), (name is I, age is 5), (name is J, age is 3), (name is J, age is 3), (name is J, age is 1)]
********** start map
{E=(name is E, age is 3), F=(name is F, age is 5), A=(name is A, age is 7), B=(name is B, age is 5), I=(name is I, age is 3), J=(name is J, age is 3)}
[A=(name is A, age is 7), B=(name is B, age is 5), E=(name is E, age is 3), F=(name is F, age is 5), I=(name is I, age is 3), J=(name is J, age is 3)]
*********start TreeSet
[(name is A, age is 2), (name is C, age is 2), (name is E, age is 2), (name is G, age is 4), (name is G, age is 8), (name is G, age is 7), (name is H, age is 2), (name is H, age is 3), (name is I, age is 6), (name is I, age is 1)]
***********start MapTree **********
{J=(name is J, age is 7), I=(name is I, age is 7), H=(name is H, age is 2), G=(name is G, age is 6), E=(name is E, age is 4), D=(name is D, age is 8), C=(name is C, age is 5)}

33.线程的五种状态

  • 新建、就绪、运行、阻塞、死亡

34.Thread类的sleep()方法和对象的wait()方法

  • sleep()是Thread的静态方法,可以使得当前线程处于阻塞状态,并将cpu资源让给其它线程,但是当前线程不会释放对象锁(如果其它线程需要这个对象锁,那么它只能等待),当前线程休眠时间结束之后,会自动变为就绪状态。
  • wait()是Object的类的方法,可以使得当前线程进入阻塞状态,并将cpu资源让给其它线程,同时释放调用wait()对象的对象锁,并且进入到这个对象的等待池中(其它线程可以使用该对象),当对象的notify()方法(或notifyAll()方法)被调用的时候,该线程才进入对象的等锁池,当线程获取到对象所之后,会自动变为就绪状态。

35.什么是进程,什么是线程

  • 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,是操作系统进行资源分配和调度的一个独立单位;线程是进程的一个实体,是CPU调度和分派的基本单位,是比进程更小的能独立运行的基本单位。线程的划分尺度小于进程,这使得多线程程序的并发性高;进程在执行时通常拥有独立的内存单元,而线程之间可以共享内存。使用多线程的编程通常能够带来更好的性能和用户体验,但是多线程的程序对于其他程序是不友好的,因为它可能占用了更多的CPU资源。当然,也不是线程越多,程序的性能就越好,因为线程之间的调度和切换也会浪费CPU时间。时下很时髦的Node.js就采用了单线程异步I/O的工作模式。

36.线程的sleep()方法和yield()方法有什么区别

  1. sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;
  2. 线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态;
  3. sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常;
  4. sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性。

37.当一个线程进入一个对象的synchronized方法A之后,其它线程是否可进入此对象的synchronized方法B?

  • 不能。其它线程只能访问该对象的非同步方法,同步方法则不能进入。因为非静态方法上的synchronized修饰符要求执行方法时要获得对象的锁,如果已经进入A方法说明对象锁已经被取走,那么试图进入B方法的线程就只能在等锁池(注意不是等待池哦)中等待对象的锁。

38.如何创建新的线程并调用

1.创建线程类:继承Thread或者实现Runnable接口(推荐后者),并重写public void run()方法。(可以通过构造函数在新建线程类的时候进行传参)
2.继承Thread类的对象可以使用.start()方式启动线程。

两者都可以将创建的对象添加到线程池中。例如:

package com.anran.thread;


/** 
 * @author anran
 * @version 创建时间:2017年9月8日 下午4:44:16
 * 类说明 : 
 */
public class SaveThread extends Thread {
    public void run(){
            System.out.println(Thread.currentThread()  );
    }
}


package com.anran.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author anran
 * @version 创建时间:2017年9月8日 下午4:47:18 类说明 :
 */
public class SaveTest {

    public static void main(String[] args) {
        // 创建一个线程池
        ExecutorService service = Executors.newFixedThreadPool(100);
        for (int i = 0; i < 100; i++) {
            // 将线程添加到线程池中
            service.execute(new SaveThread());
        }
        // 启动一次顺序关闭,执行以前提交的任务,但不接受新任务。
        service.shutdown();
        // 如果此执行程序已关闭,则返回 true。
        while (!service.isTerminated()) {
        }

    }
}

39.Callable和Future

  1. Callable类似于Runable,但是Callable可以返回对应值的功能。
  2. Future则是可以获取异步Callable返回的值。

    原码:

package com.anran.thread;

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * @author anran
 * @version 创建时间:2017年9月8日 下午6:28:16 类说明 :
 */
public class CallableAndFuture {

    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        Future<Integer> future = threadPool.submit(new Callable<Integer>() {
            public Integer call() throws Exception {
                System.out.println("in");
                Thread.sleep(5000);// 可能做一些事情
                System.out.println("out");
                return 11;
            }
        });
        System.out.println("i am working");
        try {
            System.out.println(future.get());
            System.out.println("end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

}

输出:

i am working
in
out
11
end

同时有多个线程的返回值需要获取的两种方式:
原码1:

package com.anran.thread;

import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/** 
 * @author anran
 * @version 创建时间:2017年9月8日 下午6:38:22
 * 类说明 : 
 */
public class CallableAndFuture1 {

    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newCachedThreadPool();
        CompletionService<Integer> cs = new ExecutorCompletionService<Integer>(threadPool);
        for(int i = 1; i < 5; i++) {
            final int taskID = i;
            cs.submit(new Callable<Integer>() {
                public Integer call() throws Exception {
                    return taskID;
                }
            });
        }
        // 可能做一些事情
        for(int i = 1; i < 5; i++) {
            try {
                System.out.println(cs.take().get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }

}

输出:

2
3
1
4

原码2:

package com.anran.thread;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/** 
 * @author anran
 * @version 创建时间:2017年9月8日 下午6:42:08
 * 类说明 : 
 */
public class CallableAndFuture2 {



    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newCachedThreadPool();
        List<Future<Integer>> futureList = new ArrayList<Future<Integer>>(); 
        for(int i = 1; i < 5; i++) {
            final int taskID = i;
            Future<Integer> future = threadPool.submit(new Callable<Integer>() {
                public Integer call() throws Exception {
                    //匿名内部类如果想要使用外部类中的对象,需要将外部类中的对象设置为final
                    return taskID;
                }
            });
            futureList.add(future);
        }
        // 可能做一些事情
        for(Future<Integer> future : futureList){
            try {
                System.out.println(future.get());
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
}

输出:

1
2
3
4

40.添加三种锁的方法

原因:为了防止脏数据的发生。(如银行账户中的余额,银行中的账户可以使用单例模式中的注册模式进行操作,能保证事务的原子性)

1.可以在对数据操作的方法或者代码块上添加synchronized关键字。
2.可以在对同一个对象进行操作时,对对象添加synchronized。如(synchronized (account){//此处进行操作})。
3.在对象中添加显示的锁(private Lock accountLock = new ReentrantLock();),然后在进入对应操作开始时执行(accountLock.lock();),最后在对应操作完成之后执行(accountLock.unlock();)

41. java 7新特性-TWR(Try-with-resources)

  • 在java 7之前,一般在进行文件IO操作时都需要显式的进行文件流(也可以理解为资源)的close操作,无论是操作到文件流末尾还是发生异常。往往很简单的一个逻辑都要好几行的代码进行修饰,使得代码结构变的复杂。
static String ReadFile(String file) throws IOException {
  BufferedReader br = new BufferedReader(new FileReader(file));
  try {
    return br.readLine();
  } finally {
    if (br != null) 
         br.close();
  }
}
  • try-with-resources语句是声明了一个或多个资源的try语句块。在java中资源作为一个对象,在程序完成后必须关闭。try-with-resources语句确保每个资源在语句结束时关闭。只要是实现了java.lang.AutoCloseable的任何对象(包括实现java.lang.Closeable的所有对象)都可以使用该方式对资源进行关闭。

static String ReadFile(String file) throws IOException {
  try(BufferedReader br = new BufferedReader(new FileReader(file))) {
    return br.readLine();
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值