java编程思想学习笔记(更新中)

一、对象

1.1用引用操纵对象

每种编程语言都有自己操纵内存中元素的方式。你是直接操纵对象还是间接操纵呢?C和C++中,用指针来操纵对象。
尽管Java把一切都当成对象,但操纵的标识符实际上是对象的一个“引用”。可以把这样的情景假想成用“遥控器”(引用)来操纵“电视机”(对象)。
即使没有电视机,遥控器也可以独立存在。也就是说,你拥有一个引用,并不一定需要有一个对象与它相关联。因此,如果想操纵一个句子或词,可以创建一个String引用:String s; 注意,这里的s只是一个引用,并非对象。安全的做法就是在创建一个引用的同时对其进行初始化。String s=”abcde”; 或者String s=new String(“abcde”);前者首先在栈中创建一个引用变量s,然后查看栈中是否存在“abced”,如果没有,则将“abcde”存放进栈,并让引用变量s指向它;如果有,则直接让s指向它即可;后者是java中标准的对象创建方式,其创建的对象将直接放置到堆中,每调用一次就会创建一个新的对象。

1.2必须有你创建所有的对象

1.2.1存储到什么地方

程序运行时,对象怎么进行放置安排的呢?特别是内存如何分配的?有5个不同的地方可以存储数据:
1)寄存器。最快的存储区,位于处理器内部。数量有限,故它根据需求分配。
2)堆栈。位于RAM。堆栈指针若向下移动,则分配新的内存;若向上移动,则释放那些内存。创建程序时,系统必须知道存储在堆栈中的所有数据的生命周期,以便上下移动堆栈指针。这一约束限制了程序的灵活性。
3)堆。一种通用内存池,用于存放所有java对象。相比堆栈,它编译器不需要知道存储的数据在堆里的生命周期。灵活性强。当需要一个对象时,只需用new写一行简单的代码,当执行这行代码时,胡自动在堆里进行存储分配。但为这种灵活性符出的代价是,用堆进行存储分配和清理可能比堆栈更费时。
4)常量存储。常量值通常直接存放在程序代码内部,这样做是安全的,因为它们永远不会改变。
5)非RAM存储。如果数据完全存活于程序之外,则它可以不受程序任何控制,在程序没有运行时也可以存在。这种存储方式的技巧在于:把对象转换为可以存放在其他媒介上的事物,在需要时,恢复成常规的、基于RAM的对象。

1.2.2基本类型

在程序设计中常用的一系列类型,它们需要特殊对待。之所以特殊对待,是因为new将对象存储在“堆”里,用new创建一个对象——特别是小的、简单的变量,往往不是很有效。因此,对于这些类型,java采取的方法是,不是用new来创建变量,而是创建一个并非是引用的“自动”变量。这个变量直接存储“值”,并置于堆栈中,因此更加高效。

1.3永远不需要销毁对象

事实证明,由new创建的对象,只要你需要,就会一直保留下去。这样便带来一个有趣的问题,如果java让对象继续存在,那么靠什么才能防止这些对象填满内存空间,进而阻塞你的程序?
java有一个垃圾回收器,用来监视用new创建的所有对象,并辨别那些不会再被引用的对象。随后,释放这些对象的内存空间,以便供其他新的对象使用。这样做就消除了“内存泄漏”问题,这是由于程序员忘记释放内存而产生的问题。

1.6 static关键字

通常,你必须创建一个对象,并用它来访问数据或方法。但,当声明一个事物是static时,就意味着这个域或者方法不会与包含它的那个类的任何对象实例关联在一起。所以,即使从未创建某个类的任何对象,也可以调用其static方法或访问其static域。
引用static对象有两种。一种是通过一个对象去定位它,另一种是通过类名直接引用(这对于非静态成员则不行)。
前者例子:
class Test{
        static int i=47;
}
Test t1=new Test();
Test t2=new Test();
t1.i和t2.i的值均为47。
后者例子:
Test.i++;
t1.i和t2.i的值均为48。
静态方法同理,可以通过对象调用,也可以直接用类名直接调用。

二、操作符

2.1优先级

这里写图片描述

2.2赋值

“将一个对象赋值给另一个对象”,实际是将“引用”从一个地方复制到另一个地方。这意味着若对对象使用c=d,那么c和d都指向原本只有d指向的那个对象。

三、控制执行流程

if-else

while

do-while

while和do-while的唯一区别在于do-while中的语句至少会执行一次。而在while中,若条件第一次就为false,则其中的语句一句都不执行。while比do-while常用。

for

switch

return

return有两方面用途,一个是指定一个方法返回什么值(假设它没有void返回值),另一个是它会导致当前方法的推出的退出,并返回那个值。

break

break用于强制退出循环,不执行循环中剩余的语句。

continue

continue则停止执行当前循环,然后退回循环起始处,开始下一次的迭代。

foreach语法

若想遍历某个int型数组arr中的每一个元素,一种方法是用i去遍历数组的下标实现,另一个方法是for(int i:arr){System.out.print(i+”,”);}.

四、初始化与清理

4.1用构造器确保初始化

1、构造器的命名与类名相同。
2、在创建对象时,将会为对象分配存储空间,并调用相应的构造器。这就确保在你能操作对象之前,完成对对象的初始化。
3、构造器分为无参(默认)构造器和含参构造器。
无参构造器:
class A{
    A(){
        System.out.println("Rock");
    }
}
public class Test {
    public static void main(String[] args) {
        for(int i=0;i<10;i++) {
            new A();
        }
    }
}
输出:

Rock
Rock
Rock
Rock
Rock
Rock
Rock
Rock
Rock
Rock

含参构造器:
class A{
    A(int i){
        System.out.println("Rock"+i);
    }
}
public class Test {
    public static void main(String[] args) {
        for(int i=0;i<10;i++) {
            new A(i);
        }
    }
}
输出:

Rock0
Rock1
Rock2
Rock3
Rock4
Rock5
Rock6
Rock7
Rock8
Rock9

4、构造器是一种特殊类型的方法,因为它没有返回值。这与返回值为空(void)明显不同。对于空返回值,尽管方法本身不会自动返回什么,但仍可以选择它返回别的东西。构造器则不会返回任何东西,你别无选择。

4.2方法重载

1、构造器重载和方法重载的例子:
class Tree{
    int height;
    Tree(){
        height=0;
        System.out.println("Planting a tree seed.");
    }
    Tree(int initialH){
        height=initialH;
        System.out.println("Creating new Tree that is "+height+" feet tall.");
    }
    void info() {
        System.out.println("This is "+height+" feet tall.");
    }
    void info(String s) {
        System.out.println(s+":This is "+height+" feet tall.");
    }
}
public class Test {
    public static void main(String[] args) {
        for(int i=0;i<5;i++) {
            Tree t=new Tree(i);
            t.info();
            t.info("overload method");
        }
        new Tree();
    }
}
输出:

Creating new Tree that is 0 feet tall.
This is 0 feet tall.
overload method:This is 0 feet tall.
Creating new Tree that is 1 feet tall.
This is 1 feet tall.
overload method:This is 1 feet tall.
Creating new Tree that is 2 feet tall.
This is 2 feet tall.
overload method:This is 2 feet tall.
Creating new Tree that is 3 feet tall.
This is 3 feet tall.
overload method:This is 3 feet tall.
Creating new Tree that is 4 feet tall.
This is 4 feet tall.
overload method:This is 4 feet tall.
Planting a tree seed.

2、区分重载方法

(1)每个重载方法都必须有一个独一无二的参数类型列表。可以用参数类型和参数列表的顺序加以区分。
(2)除此之外还可以用返回值来区分重载方法。

3、默认构造器

如果你写的类中没有构造器,则编译器会自动帮你创建一个默认构造器。
class Tree{
}
public class Test {
    public static void main(String[] args) {
        Tree t=new Tree();/////标注行
    }
}
标注行创建了一个对象,并调用了其默认构造器——即使你没有明确去定义它。但是如果已经定义了一个构造器(无论是否有参数),编译器就不会帮你自动创建默认构造器。
class Tree{
    Tree(int i){}
    Tree(double d){}
}
public class Test {
    public static void main(String[] args) {
        Tree t1=new Tree();/////标注行
        Tree t2=new Tree(1);
        Tree t3=new Tree(1.0);
    }
}
要是如标注行那样写,编译器就会报错:没有找到匹配的构造器。这就好比,要是你没有提供任何构造器,编译器会认为“你需要一个构造器,让我给你制造一个吧”。但是如果你已经写了一个构造器了,编译器会认为“啊,你已经写了一个构造器,所以你知道你在做什么。你是刻意省略了默认构造器。”

4、this关键字

假设你希望在方法内部获得对当前对象的引用。有一个专门的关键字:this。this关键字只能在方法内部使用,表示对“调用方法的那个对象”的引用。this的用法和其他对象的引用并无不同。但要注意,若在方法内部调用同一个类的另一个方法,就不必使用this,直接调用即可。当前方法中的this引用会自动应用于同一类中的其他方法。
只有当明确需要指出对当前对象的引用时,才需要使用this关键字。

5、构造器初始化

可以用构造器来进行初始化。但要牢记:无法阻止自动初始化的进行,它将在构造器被调用之前发生。
public class Test{
    int i;
    Test(){i=7;}
}
上述代码中,i首先会被初始化为0,然后再被初始化为7。
1)初始化顺序
在类的内部,变量定义的先后顺序决定了初始化的顺序。
class Window{
    Window(int i){System.out.println("Window("+i+")");}
}
class House {
    Window w1=new Window(1);//构造器之前
    House(){
        System.out.println("House()");
        w3=new Window(33);//w3在构造器内再次被初始化
    }
    Window w2=new Window(2);//构造器之后
    void f() {System.out.println("f()");}
    Window w3=new Window(3);//最后

}
public class Test7{
    public static void main(String[] args) {
        House h=new House();
        h.f();
    }
}
输出:

Window(1)
Window(2)
Window(3)
House()
Window(33)
f()

在House类中,故意把Window对象的定义散布到各处,以证明它们全都会在调用构造器或其他方法之前得到初始化。此外,w3在构造器内再次被初始化。
2)静态数据初始化
要想了解静态存储区域是何时初始化的,看下列例子:
class Bowl{
    Bowl(int i){
        System.out.println("Bowl("+i+")");
    }
    void f1(int i) {System.out.println("f1("+i+")");}
}
class Table{
    static Bowl bowl1=new Bowl(1);//静态数据定义
    Table(){
        System.out.println("Table()");
        bowl2.f1(1);
    }
    void f2(int i) {System.out.println("f2("+i+")");}
    static Bowl bowl2=new Bowl(2);//静态数据定义
}
class CupBoard{
    Bowl bowl3=new Bowl(3);//非静态数据定义
    static Bowl bowl4=new Bowl(4);//静态数据定义
    CupBoard(){
        System.out.println("CupBoard");
        bowl4.f1(2);
    }
    void f3(int i) {System.out.println("f3("+i+")");}
    static Bowl bowl5=new Bowl(5);//静态数据定义
}
public class Test7{
    public static void main(String[] args) {
        System.out.println("Creating new CupBorad() in main");
        new CupBoard();
        System.out.println("Creating new CupBorad() in main");
        new CupBoard();
        table.f2(1);
        cupboard.f3(1);
    }
    static Table table=new Table();//静态对象定义
    static CupBoard cupboard=new CupBoard();//静态对象定义
}
输出:

Bowl(1)
Bowl(2)
Table()
f1(1)
Bowl(4)
Bowl(5)
Bowl(3)
CupBoard
f1(2)
Creating new CupBorad() in main
Bowl(3)
CupBoard
f1(2)
Creating new CupBorad() in main
Bowl(3)
CupBoard
f1(2)
f2(1)
f3(1)

由输出可见,静态初始化只有在必要时刻才会进行。如果不创建Table对象,那么静态的bowl1和bowl2永远不会被创建。只有在第一个Table对象被创建的时候,它们才会被初始化。此后,静态对象不会再次被初始化。但是,像在CupBoard类中的bowl3是非静态的,因此,每创建一个CupBoard对象时,bowl3就会被初始化一次。
初始化的顺序是,先静态对象,而后是“非静态对象”。要执行main()(静态方法),必须加载StaticInitialization类,然后其静态域table和cupboard被初始化,这将导致它们对应的类也被加载,并且由于它们都包含静态的Bowl对象,因此Bowl随后也被加载。这样,在这个特殊的程序中的所有类在main()之前就都被加载了。

猜你喜欢

转载自blog.csdn.net/mabiao6822/article/details/82425918