《Java编程思想》笔记 - 持续更新

1、Java对于大数值,可以使用BigInteger操作大整数,使用BigDecimal指定小数的保留位数,而不损失精度。新建对象代码如下:

BigInteger bi1 = new BigInteger("12345678") ;
BigDecimal bi2 = new BigDecimal(3.333) ; 

2、如果在返回void的方法中没有return语句,那么在该方法的结尾处会有一个隐式的return。

3、执行break outer后直接跳出三层for循环然后结束循环,continue outer后继续执行剩下的循环。

public static void main(String[] args){
    outer:
    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 5; j++) {
            for (int k = 0; k < 5; k++) {
                if (j ==3){
                    break outer;
                    continue outer;
                }
                System.out.println(j);
            }
        }
    }
}

4、关于this关键字

(1).构造器Flower(String s,int petals)表明:在构造器里面尽管可以用this调用一个构造器,但却不能调用两个.此外,必须将构造器调用置于最起始处,否则编译器会报错。

(2).this的另一种用法:由于构造器参数s的名称和数据成员s的名字相同,所以会产生歧义。使用this.s来代表数据成员就能解决重名导致错误这个问题。

(3).除了在构造器中用this调用构造器之外,编译器禁止在其他任何方法中调用构造器。

5、根据继承的概念,子类会继承父类所有的成员,包括私有成员和公有成员,只不过父类的私有成员在子类中不可见,但它是确确实实存在的。

6、静态数据的初始化顺序

先上代码:

public class TestSequence {

    public static void main(String[] args){
        System.out.println("进入main方法");
        Bclass d = new Bclass("TestSequence -- main -- Bclass d");
    }

    static Bclass b = new Bclass("TestSequence -- static Bclass b ");
    static Cclass c = new Cclass("TestSequence -- static Cclass c");
}

class Bclass {
    Cclass c1 = new Cclass("Bclass -- Cclass c1");
    static Cclass c2 = new Cclass("Bclass -- static Cclass c2");

    public Bclass(String desc) {
        System.out.println(desc);
    }
}

class Cclass {
    public Cclass(String desc) {
        System.out.println(desc);
    }
}

打印结果:

Bclass -- static Cclass c2 
Bclass -- Cclass c1
TestSequence -- static Bclass b 
TestSequence -- static Cclass c
进入main方法
Bclass -- Cclass c1
TestSequence -- main -- Bclass d

结果说明:

(1)打印结果"TestSequence -- static Bclass b" --> "TestSequence -- static Cclass c" --> "进入main方法",说明类里面的静态属性会在静态main方法执行之前执行。

(2)打印结果"Bclass -- static Cclass c2" --> "Bclass -- Cclass c1" --> "TestSequence -- static Bclass b",说明一个对象被创建之前会先初始化静态属性,接下来是非静态成员属性,最后才是构造方法。

(3)打印结果"进入main方法" --> "Bclass -- Cclass c1" --> "TestSequence -- main -- Bclass d",说明静态的属性或者变量在内存中只有一份,只会被创建一次,不会被多次创建。

7、善用引用传递操作对象

public class TestReference {

    public static void main(String[] args){
        VoClass voClass = new VoClass();
        System.out.println(voClass);
        change(voClass);
        System.out.println(voClass);
    }

    private static void change(VoClass voClass){
        voClass.number = 100;
        voClass.string = "love you";
    }

}

class VoClass{
    int number = 8;
    String string = "love";

    @Override
    public String toString() {
        return "VoClass{" + "number=" + number + ", string='" + string + '\'' + '}';
    }
}

8、可变参数的使用

public static void main(String[] args){
    printMethod("one","two","three");
    printMethod();
}

private static void printMethod(String... strings){
    for (String string : strings) {
        System.out.println(string);
    }
}

打印结果:
one
two
three

9、Java文件名必须要和用public修饰的类名相同,没有用public修饰类时文件名可以随意。

10、Java的引用类型

Java对引用的概念进行了扩充,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference) 4种,这4种引用强度依次逐渐减弱。

(1)口强引用就是指在程序代码之中普遍存在的,类似"Object obj= new Object()"这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。

(2)软引用是用来描述一些还有用但并非必需的对象。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。在JDK 1.2之后,提供了SoftReference类来实现软引用。

(3)弱引用也是用来描述非必需对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当,
前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK 1.2之后,提供了WeakReference类来实现弱引用。

(4)虚引用也称为幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。在JDK 1.2之后,提供了PhantomReference类来实现虚引用。

11、final用来修饰引用,虽然引用是恒定不变的,但该引用所指向的对象自身是可以被改变的。

12、使用final修饰的变量在编译之前不一定知道他的值,但在使用之前必须初始化。如下:

private final int i = rand.nextInt(20);
static final int j = rand.nextInt(20);

总结:这个特点的好处是既可以根据不同的对象而赋不同的值,又可以保证其值恒定不变。

13、关于继承覆盖和main方法

class A {
    int a = 100;

    public A() {
        System.out.println("begin construct A");
        print();
        System.out.println("In class A  construct method, this = " + this);
    }

    public void print(){
        System.out.println("In class A , a = " + a);
    }
}

class B extends A{
    int b = 200;

    public B() {
        System.out.println("begin construct B");
        print();
        System.out.println("In class B  construct method, this = " + this);
    }

    @Override
    public void print(){
        System.out.println("In class B , b = " + b);
    }
}

class C extends B{
    int c = 300;

    public C() {
        System.out.println("begin construct C");
        print();
        System.out.println("In class C construct method, this = " + this);
    }

    @Override
    public void print(){
        System.out.println("In class C , c = " + c);
    }
}

public class MainClass {

    public MainClass() {
        System.out.println("begin construct MainClass");
    }

    public static void main(String[] args) {
        C c = new C();
        System.out.println("In main method , c = " + c);
    }
}

控制台输出如下:

begin construct A
In class C , c = 0
In class A construct method, this = C@1540e19d
begin construct B
In class C , c = 0
In class B construct method, this = C@1540e19d
begin construct C
In class C , c = 300
In class C construct method, this = C@1540e19d
In main method , c = C@1540e19d

说明:

(1)我们都知道,main方法是一个静态方法,随着JVM加载class文件时加载,先于对象而存在,也就是说不需要new MainClass就可以调用MainClass里面的mian方法,所以在控制台中我们看不到"begin construct MainClass"这句话。

(2)在子类继承父类的情形中,new一个子类时我们都知道默认会先调用父类的构造方法,上面代码可知,B继承A,C继承B,而在new C的过程中,会先调用B的构造方法,并且将C对象的引用this隐式的作为参数传到B的构造方法中,而B因为继承了A,所以在创建B对象时会继续先调用A的构造方法,同时将C对象的引用隐式的作为参数往上传递到A的构造方法中(注意是C对象的引用,而不是B对象的引用,这个通过打印的内容可得知),而B和C在继承时均覆盖了print()方法,所以在A和B的构造方法中调用的print()方法,实际上都是C对象的print()方法,因为C对象的print()方法最终覆盖了他的所有父类的print()方法。

(3)我们可以看到,前两次变量c的值均为0而并不是300,这是因为c变量是在C对象中声明并且赋值的,而在调用到A和B的构造方法时C对象还没生成,c变量还没成功赋值为300,同时从这里我们可以看出来,JVM会提前于对象为这些变量赋初值,例如0和null等这些值,以避免因非常规调用而引起的问题。

14、抽象类不能被new,他的抽象方法可以被分摊到多个抽象类去实现,或者被一个非抽象类全部实现,又或者组合实现。也就是说,顶级抽象类的抽象方法可以被其多个子抽象类去实现,假设其众多的子抽象类中没有产生新的抽象方法,如果该顶级抽象类的所有方法已经被其多个子抽象类分摊实现完了,那么最后一个继承的非抽象类不需要强制实现任何顶级抽象类的方法,可以选择性的覆盖或者不覆盖前面抽象类的非抽象方法。抽象类的好处还有,只有参数不同的共有的实现方法的实现可以放在抽象类中,根据对象的实际情况不同而表现不同的方法可以声明为抽象方法,让实现对象自己去实现。

15、普通对象和接口及抽象类之间转型

public class TestInterface {

    public static void main(String[] args){
        
        // nClass是一个InterfaceA的实现类型,直接调用one方法没问题
        InterfaceA nClass = new NClass();
        nClass.one();

        // nClass2是一个InterfaceA的实现类型,调用属于InterfaceB里面的two方法时需要转型,两种转型都可以
        InterfaceA nClass2 = new NClass();
        ((InterfaceB) nClass2).two();
        ((NClass) nClass2).two();
        
        // nClass3是一个AbstractC的实现类型,情况同上
        AbstractC nClass3 = new NClass();
        ((NClass) nClass3).one();
        
    }

}

interface InterfaceA {
    void one();
}

interface InterfaceB {
    void two();
}

abstract class AbstractC {
    abstract void three();
}

class NClass extends AbstractC implements InterfaceA,InterfaceB {

    @Override
    public void one() {
        System.out.println("one");
    }

    @Override
    public void two() {
        System.out.println("two");
    }

    @Override
    void three() {
        System.out.println("three");
    }
}

打印结果如下:

one
two
two
one

总结:继承了一个抽象类和多个接口的普通类,可以向上转型为任意一个接口类型和那个抽象类类型。

16、内部类

public class Outer {

    private int o = 10;
    public int po = 100;

    class Inner {

        private int i = 20;
        public int pi = 200;

        public void printOuterPrivateO(){
            Outer outer = new Outer();
            System.out.println("in non static Inner , outer private o = " + outer.o);
        }

        private void printOuterPublicPO(){
            System.out.println("in non static Inner , outer public po = " + po);
        }

    }

    static class StaticInner {

        private int si = 1000;

        public void printOuterPrivateO(){
            Outer outer = new Outer();
            System.out.println("in static Inner , outer private o = " + outer.o);
        }

        private void printOuterPublicPO(){
            Outer outer = new Outer();
            System.out.println("in static Inner , outer public po = " + outer.po);
        }
    }

    public static void main(String[] args){

        // 非静态内部类
        Outer outer = new Outer();
        Outer.Inner inner = outer.new Inner();
        inner.printOuterPublicPO();
        inner.printOuterPrivateO();
        System.out.println("inner private i = " + inner.i);

        // 静态内部类
        StaticInner staticInner = new StaticInner();
        staticInner.printOuterPublicPO();
        staticInner.printOuterPrivateO();
        System.out.println("staticInner private si = " + staticInner.si);
        
        // 外部类不能直接访问内部类的成员,以下做法将会报错
        System.out.println("inner public pi = " + pi);

    }
}

打印结果:

in non static Inner , outer public po = 100
in non static Inner , outer private o = 10
inner private i = 20
in static Inner , outer public po = 100
in static Inner , outer private o = 10
staticInner private si = 1000

总结:

(1)非静态内部类必须依附在外部类的实例上,所以在新建非静态内部类Inner时使用如下方式:

Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();

(2)在外部类中新建静态内部类和新建普通的类一样,如下:

StaticInner staticInner = new StaticInner();

(3)在没有新建对象的情况下,内部类可以直接访问外部类的所有非私有成员,反过来则不行。在新建对象的情况下,内部类和外部类均可以通过新建对象的方式相互访问对方的所有成员,包括私有成员。例如上面的这些代码:

//外部类访问内部类私有变量
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
System.out.println("inner private i = " + inner.i);

//内部类访问外部类私有变量
Outer outer = new Outer();
System.out.println("in static Inner , outer private o = " + outer.o);

你可能感兴趣的:(《Java编程思想》笔记 - 持续更新)