一.java随记笔记(备复习面试用)

一.java静态方法只能调用静态方法 java

1.须要知道java的编译过程。静态方法的代码在项目启动的时候就被初始化,那么,你“主方法”是静态的,项目启动时须要初始化,“被调用的方法”固然也必须是静态的,由于在“主方法”初始化的时候须要调用到“被调用的方法”,不然就没法找到“被调用方法”而致使编译失败。 若是你在非静态的方法去调用其余的方法,那么”被调用的方法“就不须要是静态的了缓存

二.Java类在new的过程当中,静态域、静态块、非静态域、非静态块、构造函数的执行顺序问题安全

1.这里先把整理好的结论抛给你们,代码实例验证请点击链接http://www.javashuo.com/article/p-ejdpgzba-ga.html。在Java类被new的过程当中,执行顺序以下:函数

  •     实现自身的静态属性和静态代码块。(根据代码出现的顺序决定谁先执行)
  •     实现自身的非静态属性和非静态代码块。
  •     执行自身的构造函数。

    在实现继承的类被new的过程当中,初始化执行顺序以下:测试

  •     实现父类的公共静态属性和静态块级代码。
  •     实现自身的静态属性和静态块级代码。
  •     实现父类的非静态属性和非静态代码块。
  •     执行父类的构造函数。
  •     实现自身的非静态属性和非静态代码块。
  •     执行自身的构造函数。

三.final的对象引用优化

使用final的限制条件和局限性.net

 当声明一个final成员时,必须在构造函数退出前设置它的值,以下:线程

public class MyClass {
  private final int myField = 3;
  public MyClass() {
    ...
  }
}对象

或者blog

public class MyClass {
  private final int myField;
  public MyClass() {
    ...
    myField = 3;
    ...
  }
}

final关键字做用在对象上,指的是该对象(堆)的引用(栈)不可变非对象不可变,也就是说该引用不能指向其余对象

须要强调的是将指向对象的成员声明为final只能将该引用设为不可变的,而非所指的对象。例如若是一个list声明以下:

private final List myList = new ArrayList();

仍然能够修改该list

myList.add("Hello");

然而,声明为final能够保证以下操做不合法:

myList = new ArrayList();
myList = someOtherList;

何时应该使用final

在匿名内部类中引用外部对象要加final修饰符呢,由于,在匿名内部类中引用的外部对象受到外部线程的做用域的制约有其特定的生命周期,
      以线程为例,当外部的变量生命周期已经完结以后,内部的线程还在运行,怎么样解决这个外部生命周期已经结束而在内部却须要继续使用呢,
      这个时候就须要在外部变量中添加final修饰符,其实内部匿名类使用的这个变量就是外部变量的一个“复制品”,即便外部变量生命周期已经结束,内部的“复制品“依然可用。

 一个答案就是“尽量的使用”。任何你不但愿改变的(基本类型,或者指向一个对象,无论该对象是否可变)通常来说都应该声明为final。另外一种看待此问题的方式是:

 若是一个对象将会在多个线程中访问而且你并无将其成员声明为final,则必须提供其余方式保证线程安全

  “其余方式”能够包括声明成员为volatile,使用synchronized或者显式Lock控制全部该成员的访问。

四.基本数据类型

java中基本类型的包装类的大部分都实现了常量池技术,即Byte,Short,Integer,Long,Character,Boolean。

这5种包装类默认建立了数值[-128,127]的相应类型的缓存数据,可是超出此范围仍然会去建立新的对象。 两种浮点数类型的包装类Float,Double并无实现常量池技术。

1.Integer

byte(字节) 	    8         -128 - 127                                           0
shot(短整型)        16      -32768 - 32768                                         0
int(整型)           32   -2147483648-2147483648                                    0
long(长整型)        64   -9233372036854477808-9233372036854477808                  0        
float(浮点型)       32  -3.40292347E+38-3.40292347E+38                            0.0f
double(双精度)	    64  -1.79769313486231570E+308-1.79769313486231570E+308        0.0d
char(字符型)        16         ‘ \u0000 - u\ffff ’                             ‘\u0000 ’
boolean(布尔型)     1         true/false                                         false

        Integer a=100,b=100;
        System.out.println(a==b);//true
        Integer aa=128,bb=-128;
        System.out.println(aa==bb);//false

JVM会自动维护八种基本类型的常量池,int常量池中初始化-128~127的范围,因此当为Integer i=127时,在自动装箱过程当中是取自常量池中的数值,而当Integer i=128时,128不在常量池范围内,因此在自动装箱过程当中需new 128,因此地址不同。

java除了基本数据类型能够直接用运算符==来比较变量的值之外,其余类型(类,包括外覆类)声明的对象若是相比较值或内容是否相等,都必须用equals,若是用==,比较的是对象引用在内存中的地址值,而不是对象引用的内容。

2.String

针对同一个对象,new出来的字符串和直接赋值给变量的字符串存放的位置是不同的,前者是在堆里面,然后者在常量池里面,另外,在作字符串拼接操做,也就是字符串相"+"的时候,得出的结果是存在在常量池或者堆里面,这个是根据状况不一样不必定的,我写了几行代码测试了一下。

    1.直接定义字符串变量的时候赋值,若是表达式右边只有字符串常量,那么就是把变量存放在常量池里面。

  2.new出来的字符串是存放在堆里面。

  3.对字符串进行拼接操做,也就是作"+"运算的时候,分2中状况:

  i.表达式右边是纯字符串常量,那么存放在栈里面。

  ii.表达式右边若是存在字符串引用,也就是字符串对象的句柄,那么就存放在堆里面。

       String str1 = "aaa";
        String str2 = "bbb";
        String str3 = "aaabbb";
        String str4 = str1 + str2;
        String str5 = "aaa" + "bbb";
        System.out.println(str3 == str4); // false
        System.out.println(str3 == str4.intern()); // true
        System.out.println(str3 == str5);// true

String类的intern()方法的做用

通常咱们变成不多使用到 intern这个方法,今天我就来解释一下这个方法是干什么的,作什么用的

首先请你们看一个例子:

    public static void main(String[] args) throws Exception {
        String a =  "b" ; 
        String b =  "b" ; 
        
        System.out.print( a == b); 
        
        String c = "d" ;
        String d = new String( "d" ).intern() ; 
        System.out.println( c == d);
    } 

你们能看出来这个例子打印在控制台的消息吗?在这里控制台输出的结果都是true  true,缘由在于 intern 这个方法返回的是 返回字符串对象的规范化表示形式,当调用 intern 方法时,若是池已经包含一个等于此 String 对象的字符串(该对象由 equals(Object) 方法肯定),则返回池中的字符串。不然,将此 String 对象添加到池中,而且返回此 String 对象的引用。这时候c和d就是相等的。

        String s1 = "ab123" ;
        String s2 = new String( "ab123" ) ;
        System.out.println( s1 == s2 ); 
        String s3 = s2.intern() ; 
        System.out.println( s1 == s3 ) ; 

public static final String A;   // 常量A
public static final String B;  // 常量B
    public static void main (String [] args) {
            // 将两个常量用+链接对s进行初始化 
            String s = A + B; 
            String t = "abcd"; 
            if (s == t) { 
                System.out.println("s等于t,它们是同一个对象"); 
                } 
            else { 
                System.out.println("s不等于t,它们不是同一个对象"); 
                } 
        //scheduledThreadPool();
    }
    static { A = "ab"; B = "cd"; } 

解释:

s不等于t,它们不是同一个对象。

A和B虽然被定义为常量,可是它们都没有立刻被赋值。在运算出s的值以前,他们什么时候被赋值,以及被赋予什么样的值,都是个变数。所以A和B在被赋值以前,性质相似于一个变量。那么s就不能在编译期被肯定,而只能在运行时被建立了。

若是改为:

public static final String A = "ab"; 
    // 常量A
 public static final String B = "cd"; 

结果为:s等于t,它们是同一个对象

String s1 = new String("xyz"); //建立了几个对象?

解释:

考虑类加载阶段和实际执行时。

(1)类加载对一个类只会进行一次。”xyz”在类加载时就已经建立并驻留了(若是该类被加载以前已经有”xyz”字符串被驻留过则不须要重复建立用于驻留的”xyz”实例)。驻留的字符串是放在全局共享的字符串常量池中的。

(2)在这段代码后续被运行的时候,”xyz”字面量对应的String实例已经固定了,不会再被重复建立。因此这段代码将常量池中的对象复制一份放到heap中,而且把heap中的这个对象的引用交给s1 持有。

这条语句建立了2个对象。

 

public class TestStringEqual {
    public static void main(String[] args) {
        String hello = "Hello";
        String lo = "lo";
        System.out.println((hello == "Hello") + " "); //true
        System.out.println((Other.hello == hello) + " "); //true
        System.out.println((other.Other.hello == hello) + " "); //true
        System.out.println((hello == ("Hel"+"lo")) + " "); //true
        System.out.println((hello == ("Hel"+lo)) + " "); //false
        System.out.println(hello == ("Hel"+lo).intern()); //true}}
        
    }
}
class Other {
    static String hello = "Hello";
}

package other;
public class Other {
    public static String hello = "Hello";
}

解释:

在同包同类下,引用自同一String对象.

在同包不一样类下,引用自同一String对象.

在不一样包不一样类下,依然引用自同一String对象.

在编译成.class时可以识别为同一字符串的,自动优化成常量,引用自同一String对象.

在运行时建立的字符串具备独立的内存地址,因此不引用自同一String对象.

3.Float类型 减法运算时精度丢失问题

   Float xx = 2.0f;
   Float yy = 1.8f;
   Float tt = xx - yy;
   System.out.println("tttttt-----" + tt);

果真输出结果是: tttttt-----0.20000005

测试了几个float类型的减法,除了*.0这样的相减没有异议以外,都存在这个问题,就是说float在相减的时候精度丢失了。后来在网上找到一段解决这个问题的办法,记在这里:

   Float xx = 2.2f;
   Float yy = 2.0f;
   Float tt = xx - yy;
  
   BigDecimal b1 = new BigDecimal(Float.toString(xx));
   BigDecimal b2 = new BigDecimal(Float.toString(yy));
   float ss = b1.subtract(b2).floatValue(); 
   System.out.println("ssss----" + ss);
   System.out.println("tttttt-----" + tt);

ssss----0.2
tttttt-----0.20000005

这样一对比,差别就很明显了。

解决了问题,再找了一下为何会产生这种差别:

网上有篇文章写得很详细,标题为《剖析float型的内存存储和精度丢失问题》,全文内容以下:

问题提出:12.0f-11.9f=0.10000038,"减不尽"为何?

如今咱们就详细剖析一下浮点型运算为何会形成精度丢失?

http://blog.csdn.net/fancylovejava/article/details/12027039

五:if里填什么能能输出ab   if(){
           System.out.println("a");
        }else{
           System.out.println("b");
        }

联想Java中print、printf、println的区别

printf主要是继承了C语言的printf的一些特性,能够进行格式化输出
print就是通常的标准输出,可是不换行
println和print基本没什么差异,就是最后会换行
System.out.printf("the number is: d",t);

参照JAVA API的定义以下:
'd' 整数 结果被格式化为十进制整数
'o' 整数 结果被格式化为八进制整数
'x', 'X' 整数 结果被格式化为十六进制整数
'e', 'E' 浮点 结果被格式化为用计算机科学记数法表示的十进制数
'f' 浮点 结果被格式化为十进制数
'g', 'G' 浮点 根据精度和舍入运算后的值,使用计算机科学记数形式或十进制格式对结果进行格式化。
'a', 'A' 浮点 结果被格式化为带有效位数和指数的十六进制浮点数
println("test")至关于print("testn")就是通常的输出字符串

printprintlnprintf的区别
print将它的参数显示在命令窗口,并将输出光标定位在所显示的最后一个字符以后。
println 将它的参数显示在命令窗口,并在结尾加上换行符,将输出光标定位在下一行的开始。
printf是格式化输出的形式。

 int i = 4;
   double j = 5;

   System.out.print("用print输出i:"+ i);
   System.out.println( "用println输出i:"+ i);
   System.out.printf("i的值为%d,j的值为%f", i,j);

 

用print输出i:4用println输出i:4
i的值为4,j的值为5.000000

能够看到,用print输出i后,没有换行,用println输出的结果直接在print输出语句后面,而输出println后换行了,因此用printf输出时,在第二行

咱们再来看printf “i的值为%d,j的值为%f”这个字符串中的"%d"变为i的值了,而"%f"变为了j的值了! 这里,"%d"的意思是一个int值的占位符,"%f"为一个double 或float值的点位符,这个变量的参数在后面提供。注意的是实参名必须按顺序排。否则就会错了。并且类型也要相符。若是咱们把语句改成System.out.printf("i的值为%d,j的值为%f", j,i);//i和j位置反了 这时就有错了由于"%d"对应的参数变为j,"%f"对应的变为i了,而j是double的,与"%d"是int形的不一致。因此有错了。 还有"%s"是一个字符串值的点位符的意思。"%c"是一个字符值的点位符的意思。 可能读者还会问为何j输出变成了5.000000?那是由于double是默认有6位小数的(这个可能和系统电脑有关,有的不是6位吧)可是若是你想只要输出两位小数行不行?能够啊!只要改下语句就好了! System.out.printf("i的值为%d,j的值为%.2f", i,j); 这里的"%.2f"的意思是输出两位小数点。若是想输出三位那就"%.3f"。 说到这里你会发现原来printf也颇有用的。这样能够控制输出的格式。

相关文章
相关标签/搜索