JDK和JRE的区别

JRE=JVM+java核心类库
JDK=JRE+java开发工具(javac.exe java.exe javadoc.exe)

Java中是如何支持正则表达式操作的
Java中的String类提供了支持正则表达式操作的方法,包括:matches()、replaceAll()、replaceFirst()、split()。此外,Java中可以用Pattern类表示正则表达式对象,它提供了丰富的API进行各种正则表达式操作,如:

import java.util.regex.Matcher;
import java.util.regex.Pattern;
class RegExpTest {
    public static void main(String[] args) {
        String str = "成都市(成华区)(武侯区)(高新区)";
        Pattern p = Pattern.compile(".*?(?=\\()");
        Matcher m = p.matcher(str);
        if(m.find()) {
            System.out.println(m.group());
        }
    }
}

正则表达式用途

在编写处理字符串的程序时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。计算机处理的信息更多的时候不是数值而是字符串,正则表达式就是在进行字符串匹配和处理的时候最为强大的工具,绝大多数语言都提供了对正则表达式的支持。

比较Java和JavaSciprt

JavaScript 与Java是两个公司开发的不同的两个产品。Java 是原Sun Microsystems公司推出的面向对象的程序设计语言,特别适合于互联网应用程序开发;而JavaScript是Netscape公司的产品,为了扩展Netscape浏览器的功能而开发的一种可以嵌入Web页面中运行的基于对象和事件驱动的解释性语言。JavaScript的前身是LiveScript;而Java的前身是Oak语言。

下面对两种语言间的异同作如下比较:

  • 基于对象和面向对象:Java是一种真正的面向对象的语言,即使是开发简单的程序,必须设计对象;JavaScript是种脚本语言,它可以用来制作与网络无关的,与用户交互作用的复杂软件。它是一种基于对象(Object-Based)和事件驱动(Event-Driven)的编程语言,因而它本身提供了非常丰富的内部对象供设计人员使用。
  • 解释和编译:Java的源代码在执行之前,必须经过编译。JavaScript是一种解释性编程语言,其源代码不需经过编译,由浏览器解释执行。(目前的浏览器几乎都使用了JIT(即时编译)技术来提升JavaScript的运行效率)
  • 强类型变量和类型弱变量:Java采用强类型变量检查,即所有变量在编译之前必须作声明;JavaScript中变量是弱类型的,甚至在使用变量前可以不作声明,JavaScript的解释器在运行时检查推断其数据类型。
  • 代码格式不一样。

Java中如何跳出当前的多重嵌套循环

方法一:
要跳出多重循环,可以在外面的循环语句前定义一个标号,然后在里层循环体代码中使用带标号的break语句,即可跳出外层循环。例如:

tag: for(int i=0;i<10;i++){
            for(int j=0;j<10;j++){
                System.out.println("i="+i+", j="+j);
                if(j==5) break tag;
            }
        }

方法二:
让外层的循环条件表达式的结果可以受到里层循环体代码的控制,例如:

boolean flag = true;
        for (int i = 0; i < 10 && flag; i++) {
            for (int j = 0; j < 10; j++) {
                System.out.println("i=" + i + ", j=" + j);
                if (j == 5) {
                    flag = false;
                    break;
                }
            }
        }

&和&&的区别

相同点:&和&&都可以用作逻辑与的运算符,表示逻辑与(and)。

不同点:

(1)&不短路,&&短路。

(2)&两边都运算,&&左false时跳过右边(与c语言相同);

(3)&还可以用作位运算符,当&操作符两边的表达式不是boolean类型时,&表示按位与操作,我们通常使用0x0f来与一个整数进行&运算,来获取该整数的最低4个bit位,例如:0x31 & 0x0f的结果为0x01。

一个“.java”源文件中是否可以包含多个类(不是内部类)?有什么限制?

可以有多个类,但只能有一个public的类,并且public的类名必须与文件名相一致。

Switch语句能否作用在byte上,能否作用在long上,能否作用在String上?

在switch(e)中,e只能是一个整数表达式或者枚举常量体)(更大字,整数表达式可以是Int基本类型或integer包装类型,由于byte,short,char都可以隐式转换为int,所以这些类型以及这些类型的包装类型也是可以的。显然long不符合switch的语法规定,并且不能隐式的转换成int类型,所以不能作用于switch语句中。

由int类型转换为long类型是向上转换,可以直接进行隐式转换,但由long类型转换为int类型是向下转换,可能会出现数据溢出情况。

jdk1.7中switch语句可以支持String。但不建议用String,效率不高,编译中最终会转换成hashcode,实质上还是编译成了整型,case分支内又做了字符串比较。

char型变量中能不能存储一个中文汉字

char型变量是用来存储Unicode编码的字符,Unicode编码字符集中包含了汉字,所以,char型变量中当然可以存储汉子。不过,如果某个特殊汉字没有被包含在Unicode编码字符集中,那么,这个char型变量中就不能存储这个特殊汉字。补充说明:Unicode编码占用两字节,所以char型变量也是占用两字节。

使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?

使用final关键字修饰一个变量时,是指引用不能变,引用变量所指向的对象中的内容还是可以变的。例如,对于如下语句:

final StringBuffer a = new StringBuffer("immutable");
执行如下语句将报告编译器错误:
a=new StringBuffer("");
但是,执行如下语句则可以通过编译:
a.append("broken!");

Java的==与equals

String a = "abc";
String b = "abc";
String c = new String("abc");  //a==b;a!=c;a equals c;

String a = “abc”;

    创建字符串的时候先查找字符串缓冲池中有没有相同的对象,如果有相同的对象就直接返回该对象的引用,如果没有相同的对象就在字符串缓冲池中创建该对象,然后将该对象的应用返回。对于这一步而言,缓冲池中没有abc这个字符串对象,所以首先创建一个字符串对象,然后将对象引用返回给a。

String b = “abc”;

    这一句也是想要创建一个对象引用变量b使其指向abc这一对象。这时,首先查找字符串缓冲池,发现abc这个对象已经有了,这是就直接将这个对象的引用返回给b,此时a和b就共用了一个对象abc,所以a==b.不过不用担心,a改变了字符串而影响了b,因为字符串都是常量,一旦创建就没办法修改了,除非创建一个新的对象。

String c = new String(“abc”);

    c没有在字符串缓冲池中创建新的对象,但是会在内存的其他位置创建一个新的对象,所以a是指向字符串缓冲池的,c是指向内存的其他位置,两者的内存地址不同的,所以a!=c。

另外:

int a=3;                    //a==b;-128到127之间的数等于,其他不等于,缓存机制,具体下 
Integer b=new Integer(3);   //面比较int和Integer详解。

静态变量(类变量)和实例变量的区别

在语法定义上的区别:静态变量前要加static关键字,而实例变量前则不加。

在程序运行时的区别:实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。静态变量不属于某个实例对象,而是属于类,所以也称为类变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了。总之,实例变量必须创建对象后才可以通过这个对象来使用,静态变量则可以直接使用类名来引用。

public class Test {

    public static void main(String[] args) {
        Demo1 d1 = new Demo1();
        Demo1 d2 = new Demo1();
    }

}
class Demo1{
    //静态变量
    public static int a = 1;
    //实例变量
    public int b = 1;

    public Demo1(){
        a++;
        b++;
        System.out.println(a+","+b);
    }
}   
//输出
//2,2
//3,2

是否可以从一个static方法内部发出对非static方法的调用?

不可以,因为非static方法是要与对象关联在一起的,必须创建一个对象后,才可以在该对象上进行方法调用,而static方法调用的时候不需要创建对象,可以直接调用。也就是说,当一个static方法被调用时,可能还没有创建任何实例,如果从static方法发出对非static方法中发出对非static方法的调用,那个非static方法是关联到哪个对象上的呢? 这个逻辑无法成立,所以,一个static方法内部无法发出对非static方法的调用。

Integer与int的区别

int是java提供的8种数据类型之一。java为每个原始类型提供封装类,integer是Java为int提供的分装类。int的默认值为0,而Integer的默认类型为null,即integer可以区分出未赋值和值为0的区别,int则无法表达出未赋值的情况,例如想要表达出没有参加考试和考试成绩为0的区别,则只能使用integer。

另外,Integer提供了多个与整数相关的操作方法,例如,将一个字符串转换成整数,Integer中还定义了表示整数的最大值和最小值的常量。

Math.round(11.5)等于多少?Math.round(-11.5)等于多少?

math类中提供了三个与取整相关的方法:ceil、floor、round,这些方法的作用与它们的英文名称相对应,

ceil的英文意义是天花板,该方法就表示向上取整,Math.ceil(11.3)的结果为12,Math.ceil(-11.3)的结果为-11;

floor的英文意义是地板,该方法就表示向下取整,Math.floor(11.6)的结果为11,Math.floor(-11.6)的结果是-12;

round方法表示“四舍五入”,算法为math.floor(x+0.5),即将原来的数字加上0.5后再向下取整,所以,Math.round(11.5)的结果为12,Math.round(-11.5)的结果为-11.

代码优化

if(username.equals("zxx"))
//username可能为NULL,会报空指针错误,改为“zxx".equals(username)

public,private,protected

作用域 当前类 同一包 子孙类 其他包
public √ √ √ √
protected √ √ √ ×
friendly √ √ × ×
private √ × × ×

说明:如果在修饰的元素上面没有写任何访问修饰符,则表示friendly。

**20.接口是否可继承接口?抽象类是否可实现(implements)接口?抽象类是否可继承具体类(concrete class)?抽象类中是否可以有静态的main方法?
**

接口可继承接口;抽象类可以实现接口;抽象类可以继承具体类;抽象类中可以有静态的main方法。

abstract抽象方法(大括号改为分号)必须存放在抽象类中,抽象类中可以存在非抽象方法,可以不存在抽象方法;抽象类不可new对象;抽象类方法要被使用,子类必须复写所有抽象方法,否则子类还是抽象类。

抽象类和普通类的唯一区别:就是不能创建实例对象和允许有abstract方法。

接口被实现,抽象类被继承。

clone()方法

clone在堆上分配内存,分配的内存和源对象(即调用clone方法的对象)相同,然后再使用原对象中对应的各个域,填充新对象的域, 填充完成之后,clone方法返回,一个新的相同的对象被创建,同样可以把这个新对象的引用发布到外部。如果,想要对该对象进行处理,又想保留原来数据进行接下来的操作,clone就很方便。

clone有缺省(默认)行为
super.clone();
因为首先要把父类中的成员复制到位,然后才是复制自己的成员。

浅拷贝创建一个新对象,但两者成员变量扔指向同一对象,仍存在联系。

class Head implements Cloneable{    //深拷贝
    String s;
    void set(String s1) {
        s = s1;
    }
    protected Object clone() throws CloneNotSupportedException{
        return super.clone();
    }
}
public class Person implements Cloneable{
    
    Head head;
    Person(Head head) {
        this.head = head;
    }
    protected Object clone() throws CloneNotSupportedException{
        Person p = (Person)super.clone();
        p.head = (Head)head.clone();
        return p;
    }
    public static void main(String[] args) throws CloneNotSupportedException{
        Person p = new Person(new Head());
        Person p1 = (Person)p.clone();
        System.out.println("p == p1 " + (p == p1));
        System.out.println("p.head == p1.head " + (p.head == p1.head));
    }

}

面向对象的特征

面向对象的编程语言有封装、继承 、抽象、多态等4个主要的特征。

  • 抽象:

我们在定义一个类的时候,实际上就是把一类事物的公有的属性和行为提取出来,形成一个物理模型,这种研究问题的方法称为抽象。

  • 封装:

封装就是把抽象的数据和对数据进行的操作封装在一起,数据被保存在内部,程序的其他部分只有通过被授权的操作(成员方法)才能对数据进行操作。

电视机的开关,对音量,颜色,频道的控制是公开的,谁都可以操作,但是对机箱后盖,主机板的操作却不是公开的,一般是由专业维修人员来玩。

  • 继承:

继承可以解决代码复用问题,让我们编程更加靠近人类的思维,当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类(比如刚才的Student),在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extend语句来声明继承 父类:

class 子类 extends 父类

这样,子类就会自动拥有父类定义的属性和方法。

java中类间不支持多继承(一个继承多个,不同父类可能有同名同参同返方法,会混。解决办法:内部类,创建一个类,类内创建多个内部类分别继承,创建对象写函数调用即可),支持多层继承、多实现;接口与接口间支持多继承。

  • 多态:

所谓多态,就是指一个引用(类型)在不同情况下的多种状态,你也可以这样理解:多态是指通过指向父类的指针,来调用在不同子类中实现的方法。

多态是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。

多态的好处:

后面创建的类是前面创建的那个类的子类,就可以通过父类创建的对象对子类的方法进行访问,一个对象可以访问多个方法。

实现多态的机制

父类或者接口定义的引用变量可以指向子类或者具体实现类的实例对象,由于程序调用方法是在运行期才动态绑定的,那么引用变量所指向的具体实例对象在运行期才确定。所以这个对象的方法是运行期正在内存运行的这个对象的方法而不是引用变量的类型中定义的方法。

静态分派:所有依赖静态类型来定位方法执行版本的分派动作。静态分派发生在编译阶段,因此确定静态分派的动作实际上不是由虚拟机来执行的,而是由编译器来完成。(编译时多态)

动态分派:在运行期根据实际类型确定方法执行版本的分派动作。(运行时多态)

重载是编译时多态,重写是运行时多态。

接口中定义变量必须为public static final的原因

在interface里面的变量默认都是public static final 的,原因如下:

接口是一种高度抽象的"模版",而接口中的属性也就是’模版’的成员,就应当是所有实现"模版"的实现类的共有特性,所以它是public的 ,是所有实现类共有的.

使用static是防止一个类可以多个接口时候出现重名的变量,无法区分导致编译错误。

接口方法都是抽象的,可变的东西都应该归属到实现类中,所以接口的变量和方法都应是不可修改的。接口只是对事物的属性和行为更高层次的抽象,对修改关闭,对扩展(不同的实现implements)开放。

abstract class和interface区别

含有abstract修饰符的class即为抽象类,abstract 类不能创建的实例对象。含有abstract方法的类必须定义为abstract class,abstract class类中的方法不必是抽象的。abstract class类中定义抽象方法必须在具体(Concrete)子类中实现,所以,不能有抽象构造方法或抽象静态方法。如果的子类没有实现抽象父类中的所有抽象方法,那么子类也必须定义为abstract类型。

接口(interface)可以说成是抽象类的一种特例,接口中的所有方法都必须是抽象的。接口中的方法定义默认为public abstract类型,接口中的成员变量类型默认为public static final。

下面比较一下两者的语法区别:

1.抽象类可以有构造方法,接口中不能有构造方法。

2.抽象类中可以有普通成员变量,接口中没有普通成员变量(public static final)

3.抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。

4.抽象类中的抽象方法的访问类型可以是public,protected,但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。

5.抽象类中可以包含静态方法,接口中不能包含静态方法

6.抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。

7.一个类可以实现多个接口,但只能继承一个抽象类。

abstract的method是否可同时是static,是否可同时是native,是否可同时是synchronized

都不能。

static方法是不会被覆盖的,而abstract方法正是要子类去覆盖它,所以也是没有意义的。

abstract为没有实现的方法,native为本机实现的方法,自相矛盾。一个native方法就是一个Java调用非Java代码的接口。一个native方法是指该方法的实现由非Java语言实现,比如用C或C++实现。

abstract方法没有实现,也不可能实际调用抽象方法,没有必要synchronized修饰,当然子类可以根据需要同步该方法.

**接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继
承实体类(concrete class)?**

  接口可以继承接口。抽象类可以实现(implements)接口,抽象类可继承实
体类,但前提是实体类必须有明确的构造函数。

启动一个线程是用run()还是start()?

调用start()方法会自动运行线程中复写的run()方法中代码。

启动线程肯定要用start()方法。当用start()开始一个线程后,线程就进入就绪状态,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。当cpu分配给它时间时,才开始执行run()方法(如果有的话)。start()是方法,它调用run()方法.而run()方法是你必须重写的. run()方法中包含的是线程的主体。 

继承Thread类的启动方式: 

public class ThreadStartTest { 
    public static void main(String[] args) { 
        ThreadTest tt = new ThreadTest();// 创建一个线程实例 
         tt.start();  // 启动线程 
    } 
}

实现Runnable接口的启动方式: 

public class RunnableStartTest { 
    public static void main(String[] args) { 
       Thread t = new Thread(new RunnableTest());    // 创建一个线程实例 
        t.start();  // 启动线程 
    } 
}

实际上这两种启动线程的方式原理是一样的。首先都是调用本地方法启动一个线程,其次是在这个线程里执行目标对象的run()方法。那么这个目标对象是什么呢?为了弄明白这个问题,我们来看看Thread类的run()方法的实现: 

public void run() { 
    if (target != null) { 
        target.run(); 
    } 
}

当我们采用实现Runnable接口的方式来实现线程的情况下,在调用new Thread(Runnable target)构造器时,将实现Runnable接口的类的实例设置成了线程要执行的主体所属的目标对象target,当线程启动时,这个实例的 run()方法就被执行了。当我们采用继承Thread的方式实现线程时,线程的这个run()方法被重写了,所以当线程启动时,执行的是这个对象自身的 run()方法。总结起来就一句话,如果我们采用的是继承Thread类的方式,那么这个target就是线程对象自身,如果我们采用的是实现Runnable接口的方式,那么这个target就是实现了Runnable接口的类的实例。

int和Integer有什么区别

1、Integer是int的包装类,int则是java的一种基本数据类型

2、Integer变量必须实例化后才能使用,而int变量不需要

3、Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值

4、Integer的默认值是null,int的默认值是0

延伸:

关于Integer和int的比较

1、由于Integer变量实际上是对一个Integer对象的引用,所以两个通过new生成的Integer变量永远是不相等的(因为new生成的是两个对象,其内存地址不同)。

Integer i = new Integer(100);
Integer j = new Integer(100);
System.out.print(i == j); //false

2、Integer变量和int变量比较时,只要两个变量的值是向等的,则结果为true(因为包装类Integer和基本数据类型int比较时,java会自动拆包装为int,然后进行比较,实际上就变为两个int变量的比较)

Integer i = new Integer(100);
int j = 100;
System.out.print(i == j); //true

3、非new生成的Integer变量和new Integer()生成的变量比较时,结果为false。(因为非new生成的Integer变量指向的是java常量池中的对象,而new Integer()生成的变量指向堆中新建的对象,两者在内存中的地址不同)

Integer i = new Integer(100);
Integer j = 100;
System.out.print(i == j); //false

4、对于两个非new生成的Integer对象,进行比较时,如果两个变量的值在区间-128到127之间,则比较结果为true,如果两个变量的值不在此区间,则比较结果为false

Integer i = 100;
Integer j = 100;
System.out.print(i == j); //true
Integer i = 128;
Integer j = 128;
System.out.print(i == j); //false

java对于-128到127之间的数,会进行缓存,Integer i = 127时,会将127进行缓存,下次再写Integer j = 127时,就会直接从缓存中取,就不会new了.

String,StringBuffer与StringBuilder的区别

String底层是一个final类型的字符数组,所以String的值是不可变的,每次对String的操作都会生成新的String对象,造成内存浪费。

StringBuffer是可变类,效率低,是线程安全的字符串操作类。任何对它指向的字符串的操作都不会产生新的对象。每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量。

StringBuilder可变字符序列、效率高、线程不安全。

小结:
(1)如果要操作少量的数据用 String;

(2)多线程操作字符串缓冲区下操作大量数据 StringBuffer;

(3)单线程操作字符串缓冲区下操作大量数据 StringBuilder(推荐使用)。

StringBuffer和StringBuilder都继承了AbstractStringBuilder抽象类。String是最基本的数据类型吗

基本数据类型包括byte、int、char、long、float、double、boolean和short,共八个。

java.lang.String类是final类型的,因此不可以继承这个类、不能修改这个类。为了提高效率节省空间,我们应该用StringBuffer类。

大O符号(big-O notation)

大O符号(Big O notation), 又称渐进符号,是用于描述函数的渐近行为的数学符号。它是指用另一个(通常更简单的)函数来描述一个函数数量级的渐进上界。

大O描述当数据结构中的元素增加时,算法的规模和性能在最坏情景下有多好。数据结构中常用表示算法时间复杂度,空间复杂度。

大O还可以描述其它行为,比如内存消耗。因为集合类实际上是数据结构,因此我们一般使用大O符号基于时间,内存,性能选择最好的实现。大O符号可以对大量数据性能给予一个很好的说明。

数组(Array)和列表(ArrayList)的区别

(1)ArrayList是Array的复杂版本

ArrayList内部封装了一个Object类型的数组,从一般的意义来说,它和数组没有本质的差别,甚至于ArrayList的许多方法,如Index、IndexOf、Contains、Sort等都是在内部数组的基础上直接调用Array的对应方法。

(2)存储的数据类型

ArrayList可以存储异构对象,而Array只能存储相同数据类型的数据。

(3)长度的可变

Array的长度实际上是不可变的,二维变长数组实际上的长度也是固定的,可变的只是其中元素的长度。而ArrayList的长度既可以指定(即使指定了长度,也会自动2倍扩容)也可以不指定,是变长的。

(4)存取和增删元素

对于一般的引用类型来说,这部分的影响不是很大,但是对于值类型来说,往ArrayList里面添加和修改元素,都会引起装箱和拆箱的操作,频繁的操作可能会影响一部分效率。另外,ArrayList是动态数组,它不包括通过Key或者Value快速访问的算法,所以实际上调用IndexOf、Contains等方法是执行的简单的循环来查找元素,所以频繁的调用此类方法并不比你自己写循环并且稍作优化来的快,如果有这方面的要求,建议使用Hashtable或SortedList等键值对的集合。

值传递和引用传递

值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量.
引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身 。 所以对引用对象进行操作会同时改变原对象.
一般认为,java内的传递都是值传递.

详解基本数据类型

基本类型,或者叫做内置类型,是Java中不同于类(Class)的特殊类型。它们是我们编程中使用最频繁的类型。

Java是一种强类型语言,第一次申明变量必须说明数据类型,第一次变量赋值称为变量的初始化。

Java基本类型共有八种,基本类型可以分为三类:

字符类型char

布尔类型boolean

整数类型byte、short、int、long

浮点数类型float、double。

Java中的数值类型不存在无符号的,它们的取值范围是固定的,不会随着机器硬件环境或者操作系统的改变而改变。

实际上,Java中还存在另外一种基本类型void,它也有对应的包装类java.lang.Void,不过我们无法直接对它们进行操作。

基本数据类型有什么好处?

我们都知道在Java语言中,new一个对象是存储在堆里的,我们通过栈中的引用来使用这些对象;所以,对象本身来说是比较消耗资源的。

对于经常用到的类型,如int等,如果我们每次使用这种变量的时候都需要new一个Java对象的话,就会比较笨重。

所以,和C++一样,Java提供了基本数据类型,这种数据的变量不需要使用new创建,他们不会在堆上创建,而是直接在栈内存中存储,因此会更加高效。

byte:byte用1个字节来存储,范围为-128(-2^7)到127(2^7-1),在变量初始化的时候,byte类型的默认值为0。

short:short用2个字节存储,范围为-32,768 (-2^15)到32,767 (2^15-1),在变量初始化的时候,short类型的默认值为0,一般情况下,因为Java本身转型的原因,可以直接写为0。

int:int用4个字节存储,范围为-2,147,483,648 (-2^31)到2,147,483,647 (2^31-1),在变量初始化的时候,int类型的默认值为0。

long:long用8个字节存储,范围为-9,223,372,036,854,775,808 (-2^63)到9,223,372,036, 854,775,807 (2^63-1),在变量初始化的时候,long类型的默认值为0L或0l,也可直接写为0。

超出范围会溢出。溢出的时候并不会抛异常,也没有任何提示。所以,在程序中,使用同类型的数据进行运算的时候,一定要注意数据溢出的问题。

包装类型

基本数据类型 包装类
byte Byte
boolean Boolean
short Short
char Character
int Integer
long Long
float Float
double Double
在这八个类名中,除了Integer和Character类以后,其它六个类的类名和基本数据类型一致,只是类名的第一个字母大写即可。

为什么需要包装类?
很多人会有疑问,既然Java中为了提高效率,提供了八种基本数据类型,为什么还要提供包装类呢?

这个问题,其实前面已经有了答案,因为Java是一种面向对象语言,很多地方都需要使用对象而不是基本数据类型。比如,在集合类中,我们是无法将int 、double等类型放进去的。因为集合的容器要求元素是Object类型。

为了让基本类型也具有对象的特征,就出现了包装类型,它相当于将基本类型“包装起来”,使得它具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作。

自动拆装包

把基本数据类型转换成包装类的过程就是打包装,英文对应于boxing,中文翻译为装箱。

反之,把包装类转换成基本数据类型的过程就是拆包装,英文对应于unboxing,中文翻译为拆箱。

在Java SE5中,为了减少开发人员的工作,Java提供了自动拆箱与自动装箱功能。

自动装箱: 就是将基本数据类型自动转换成对应的包装类。

自动拆箱:就是将包装类自动转换成对应的基本数据类型。

Integer i =10;  //自动装箱
int b= i;     //自动拆箱

Integer i=10 可以替代 Integer i = new Integer(10);,这就是因为Java帮我们提供了自动装箱的功能,不需要开发者手动去new一个Integer对象。

自动装箱都是通过包装类的valueOf()方法来实现的.自动拆箱都是通过包装类对象的xxxValue()来实现的。

注意事项:

包装对象的数值比较,不能简单的使用==,虽然-128到127之间的数字可以,但是这个范围之外还是需要使用equals比较。

前面提到,有些场景会进行自动拆装箱,同时也说过,由于自动拆箱,如果包装类对象为null,那么自动拆箱时就有可能抛出NPE。空指针异常。

如果一个for循环中有大量拆装箱操作,会浪费很多资源。

为什么会出现4.0-3.6=0.40000001

2进制的小数无法精确的表达10进制小数,计算机在计算10进制小数的过程中要先转换为2进制进行计算,这个过程中出现了误差。

十进制的数在内存中是怎么存的

以二进制补码形式存储,最高位是符号位,正数的补码是它的原码,负数的补码是它的反码加1,在求反码时符号位不变,符号位为1,其他位取反

Lamda表达式

基础语法在java笔记中。

Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更优雅的 Java 代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。JDK 也提供了大量的内置函数式接口供我们使用,使得 Lambda 表达式的运用更加方便、高效。

虽然使用 Lambda 表达式可以对某些接口进行简单的实现,但并不是所有的接口都可以使用 Lambda 表达式来实现。Lambda 规定接口中只能有一个需要被实现的方法,不是规定接口中只能有一个方法。

jdk 8 中有另一个新特性:默认方法default, 被 default 修饰的方法会有默认实现,不是必须被实现的方法,所以不影响 Lambda 表达式的使用。

优点:1. 简洁。2. 非常容易并行计算。3. 可能代表未来的编程趋势。

缺点:1. 若不用并行计算,很多时候计算速度没有比传统的 for 循环快。(并行计算有时需要预热才显示出效率优势)2. 不容易调试。3. 若其他程序员没有学过 lambda 表达式,代码不容易让其他语言的程序员看懂。

java8的新特性

Lambda 表达式 − Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中。

方法引用− 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。

默认方法−默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。我们只需在方法名前面加个 default 关键字即可实现默认方法。

新工具− 新的编译工具,如:Nashorn引擎 jjs(基于标准Nashorn引擎的命令行工具,可以接受js源码并执行。)、 类依赖分析器jdeps(java dependencies,jdeps命令显示Java类文件的包级或类级依赖关系。)。

Stream API −新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。

Date Time API − 加强对日期与时间的处理。

Optional 类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。

Nashorn, JavaScript 引擎 − Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用。

符号“==”比较的是什么

“==”对比两个对象基于内存引用,如果两个对象的引用完全相同(指向同一个对象)时,“==”操作将返回true,否则返回false。“==”如果两边是基本类型,就是比较数值是否相等。

Object若不重写hashCode()的话,hashCode()如何计算出来的?

Object 的 hashcode 方法是本地方法,也就是用 c 语言或 c++ 实现的,该方法直接返回对象的 内存地址。

为什么重写equals还要重写hashcode

为了遵守:2个对象equals,那么 hashCode一定相同规则。参与equals函数的字段,也必须都参与hashCode 的计算。

equals方法:

Object类中默认的实现方式是 : return this == obj 。那就是说,只有this 和 obj引用同一个对象,才会返回true。

而我们往往需要用equals来判断2个对象是否等价,而非验证他们的唯一性。这样我们在实现自己的类时,就要重写equals.

按照约定,equals要满足以下规则。

自反性: x.equals(x) 一定是true

对null: x.equals(null) 一定是false

对称性: x.equals(y) 和 y.equals(x)结果一致

传递性: a 和 b equals , b 和 c equals,那么 a 和 c也一定equals。

一致性: 在某个运行时期间,2个对象的状态的改变不会影响equals的决策结果,那么,在这个运行时期间,无论调用多少次equals,都返回相同的结果。

hashCode方法返回值是int类型的散列码。

在某个运行时期间,只要对象的(字段的)变化不会影响equals方法的决策结果,那么,在这个期间,无论调用多少次hashCode,都必须返回同一个散列码。

如果2个对象通过equals调用后返回是true,那么这个2个对象的hashCode方法也必须返回同样的int型散列码。

如果2个对象通过equals返回false,他们的hashCode返回的值允许相同。

1、如果两个对象equals,Java运行时环境会认为他们的hashcode一定相等。

2、如果两个对象不equals,他们的hashcode有可能相等。(equals直接返回false,不会报错)

3、如果两个对象hashcode相等,他们不一定equals。

4、如果两个对象hashcode不相等,他们一定不equals。

重写hashCode时注意事项

重写hashCode方法时除了上述一致性约定,还有以下几点需要注意:

(1)返回的hash值是int型的,防止溢出。

(2)不同的对象返回的hash值应该尽量不同。(为了hashMap等集合的效率问题)

(3)《Java编程思想》中提到一种情况

“设计hashCode()时最重要的因素就是:无论何时,对同一个对象调用hashCode()都应该产生同样的值。如果在讲一个对象用put()添加进HashMap时产生一个hashCdoe值,而用get()取出时却产生了另一个hashCode值,那么就无法获取该对象了。所以如果你的hashCode方法依赖于对象中易变的数据,用户就要当心了,因为此数据发生变化时,hashCode()方法就会生成一个不同的散列码”。

map的分类和常见的情况

java为数据结构中的映射定义了一个接口java.util.Map;它有四个实现类,分别是HashMap Hashtable LinkedHashMap 和TreeMap.

Map主要用于存储健值对,根据键得到值,因此不允许键重复(重复了覆盖了),但允许值重复。

Hashmap 是一个最常用的Map,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是完全随机的。 HashMap最多只允许一条记录的键为Null;允许多条记录的值为 Null;

HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap;可能会导致数据的不一致。如果需要同步,可以用 Collections的synchronizedMap方法使HashMap具有同步的能力,或者使用ConcurrentHashMap。

Hashtable与 HashMap类似,它继承自Dictionary类,不同的是:它不允许记录的键或者值为空;它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了 Hashtable在写入时会比较慢。

LinkedHashMap 是HashMap的一个子类,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.也可以在构造时用带参数,按照应用次数排序。在遍历的时候会比HashMap慢,不过有种情况例外,当HashMap容量很大,实际数据较少时,遍历起来可能会比 LinkedHashMap慢,因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关。

TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。

总结:

一般情况下,我们用的最多的是HashMap,在Map 中插入、删除和定位元素,HashMap 是最好的选择。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。如果需要输出的顺序和输入的相同,那么用LinkedHashMap 可以实现,它还可以按读取顺序来排列.