Java笔记4
这篇的主要内容是查漏补缺。原因是今天想继续下一部分:继承和多态,结果发现林的书有许多东西讲的太精简,甚至有失偏颇。所以下面我将结合李的书和博客多前面的内容进行回顾。
1.数组
内存中的数组
数组引用变量只是一个引用,类似于指针,它存放了对象的真是地址,但是这只是类比理解,实际上还是有差别的。实际的数组对象存放在堆中,而引用变量如果是一个局部变量就是在栈内存中。(是不是又像c++)同样的,当一个方法执行时,每个方法都会建立内存栈,然后将变量依次放入栈中,当使用完时也就被销毁了。堆中的对象是不会这样的,只要有引用指向它,它就会存在,当没有任何引用指向它时,它就会被垃圾管理器回收。这和C++不一样,C++中的需要自己手动创建,手动释放。
引用的指定并不是唯一的,不是我被指定给了某个对象就不能再被指定给别人,只要类型相符就没有问题。
基本类型数组的初始化
对于基本类型数组,数组元素的值是直接存在对应的数组元素中的,因此,初始化数组时先为该数组分配内存空间,然后将数组的值放入里面就行。注意细节:分配空间后,里面并不是空的,而是默认值,关于默认值前面的笔记提到过。
引用类型数组的初始化
先给出代码:
定义类:
1 | class Person{ |
测试类:
1 | public class ReferenceArrayTest { |
结果非常简单。问题在于下面这张图:
注意图的左边部分,是不是觉得很变扭,我总觉得这里不对。后来查到,引用变量是放在栈中的。关于内存空间分配,参看链接(我还没研究过相关内容,不保证文章的正确):
[Java的内存分配]:https://www.cnblogs.com/SaraMoring/p/5687466.html
this的使用
1 | public class Dog { |
谁在调用这个方法,this就代表谁。 在上面的代码中,假如我创建了一个dog
对象,this.jump()
代表调用dog的jump()方法,这种在同一个对象中的两个方法之间的依赖,在用的时候可以省略this。
省略this前缀只是一种假象,虽然程序员省略了调用jump()方法之前的this ,但实际上这个this依然是存在的。根据汉语语法习惯:完整的语句至少包括主语、谓语、宾语,在面向对象的世界里,主、谓、宾的结构完全成立,例如 “猪八戒吃西瓜”是一条汉语语句,转换为面向对象的语法,就可以写成“猪八戒.吃(西瓜);”,因此本书常常把调用成员变量、方法的对象称为“主调(主语调用者的简称)”。对于Java语言来说,调用成员变量、方法时,主调是必不可少的,即使代码中省略了主调,但实际的主调依然存在。一般来说,如果调用static修饰的成员(包括方法、成员变量)时省略了前面的主调,那么默认使用该类作为主调;如果调用没有static修饰的成员(包括方法、成员变量)时省略了 前面的主调,那么默认使用this作为主调。
关于静态与非静态
1 | public class StaticAccessNoStatic { |
main()方法是静态的,它是属于类的,info()是属于对象的方法,必须使用对象来调用。类比上一节的代码,main方法调用info方法时,相当于调用this.info()
方法,但是再三强调过了,static是类的,不属于对象,也就无从调用。而且就算允许了,那么,this是没法引用有效的对象的。
但是,但是,最不合理的地方就是:
1 | Dog dog = new Dog(); |
假定run()方法是static的,你是可以通过对象dog来调用的。但是,必须杜绝这种写法,正确的做法是:Dog.run;
。
还有一些特殊情况,例如:当类的成员变量和类的方法的局部变量同名时,就需要用到this。
1 | public class Dog{ |
所以说,在构造函数(构造器)中的this就是这么来的。
1 | public class Dog { |
在构造中,this是指new出来的新对象。
1 | public class CashCard{ |
构造器是被new调用的,所以this指代的是这个新对象。
1 | public class CashCardTest{ |
可以得出推测,谁在调用含有this的方法,this就代表了谁。
1 | public class ReturnThis { |
从grow()方法的返回值类型也可以看出,this是指代的调用它的rt。当执行了第一次调用后,原本的rt.grow().grow().grow();
意思就是rt.grow().grow();
,只不过现在的rt中的成员变量值已经改变。因为返回的是一个对象,所以可以连续调用。
关于方法的一些思考
方法很像函数,在C++中函数是老大,一切功能都是依靠函数,但在Java中类是老大,一切方法都必须在类或对象中。方法的调用是通过“类.方法”或“对象.方法”,必须通过方法和类。即使是调用同一个类中的不同方法,如果是static的,是依靠类,如果是普通的,是依靠this。
关于值传递
在笔记3中我贴了两篇有关的博客,说的非常清楚了。值传递传递的是值的拷贝,对于基本类型,这会造成你修改了传递过去的拷贝值并不会影响原来的值,但是对于引用对象来说,确实是传递了值——引用,但并没有拷贝对象(没有new),所以修改拷贝值指向的对象是会影响到原来的对象的。
成员变量与局部变量
一个是类中的变量,另一个是方法中的变量。成员变量也可以加上static,就成了类变量,同样的具有类方法的相关表现。还是上面说过的坚决杜绝通过实例对象访问类的静态的东西,不论方法还是成员。
封装与隐藏
访问控制符
private,protect,public三个,初次之外其实还有个default。
private:如果类中的成员,包括成员变量,方法,构造器等,使用这个关键字,则这个成员只能在当前类中使用。用来修饰成员变量最合适。
default:如果类中的成员,包括成员变量,方法,构造器等或者一个外部类不使用任何访问控制符,它则是包访问权限,这个成员只能在当前包中使用。说的有点绕,其实就是没有任何控制符的成员只能在同一个包中使用。
protected:如果类中的成员,包括成员变量,方法,构造器等,使用这个关键字,则这个成员既可以被同一个包中的其他类使用,也可以被不同包中的子类使用。
public:最宽松的访问控制,不论是否在同一个包中,是否具有父子继承关系,不论在哪都可以使用。
private | default | protected | public | |
---|---|---|---|---|
同一个类中 | Y | Y | Y | Y |
同一个包中 | Y | Y | Y | |
子类中 | Y | Y | ||
全局范围内 | Y |
*一个Java源文件中如果有一个public类,那么文件名必须是这个类名,不然,在别的地方要怎么调用这个类呢。
关于访问控制符有以下几个原则:
1.类里的绝大部分成员变量都应该使用private,只有一些static修饰的,类似全局变量的成员变量,才考虑用public。还有一些辅助类的其他方法的方法(工具方法)也应该是private。
2.如果某个类主要用于做为父类,类里的大部分方法只被用来让子类重写,而不像被外界调用,则应该用protected。
关于package
在没有import package时,在一个父包中使用子包中的类,也必须写完整的包路径和类名。父包和子包确实有内在的逻辑关系,通常子包是父包的一个模块,但这并不代表在用法上有任何关系,使用时必须用完整的名字。
可以使用import导入包,import java.util.*
表示导入这个包下面的所有类,不包括下面的子包。
但不是全部导入就行,因为可能存在不同的包中有相同的类名,还是需要在使用时显式的指定。
再论构造器
构造器是构建Java对象的重要手段,但是,要强调的是,不是构造器新建了Java对象。根本的还是new关键字。ReturnThis rt = new ReturnThis();
可以看出,最初额内存空间是有new创建的,而构造器进一步对这个空间内的东西进行创建。