Java基础学习拾遗
这是我根据个人需要选取的一些零散知识点,并不成体系,但具有一定的启发性。
在这里尽可能简洁地记录一些基础学习过程中的零散知识点。
拾遗
必要的计算机设置
这些简单的设置可以为编码工作带来便利。以WinXP为例。
- 文件夹视图改为详细信息,并应用到所有;
- 显示所有文件(夹);
- 不隐藏文件扩展名;
- 地址栏显示完整路径
标识符
字母,下划线,美刀和数字构成,前三种可以开头
注意:integer是一个合法标识符。
深入理解变量的实质
变量绝对不是一块内存
下面的内容引自《C++从零开始》,对变量做了深入揭示。
变量是一个映射元素。映射表由编译器维护,表中的每一行都是这个表的一个元素(也称记录)。
表有三列:变量名、对应地址和相应类型。变量名是一个标识符,其命名规则按照上面所说的来。
当要对某块内存写入数据时,程序员使用相应的变量名进行内存的标识,而表中的对应地址就记录了这个地址,进而将程序员给出的变量名,一个标识符,映射成一个地址,因此变量是一个映射元素。而相应类型则告诉编译器应该如何解释此地址所指向的内存,是2个连续字节还是4个?是原码记录还是补码?而变量所对应的地址所标识的内存的内容叫做此变量的值。
有如下的变量解释:“可变的量,其相当于一个盒子,数字就装在盒子里,而变量名就写在盒子外面,这样电脑就知道我们要处理哪一个盒子,且不同的盒子装不同的东西,装字符串的盒子就不能装数字。”上面就是我第一次学习编程时,书上写的(是BASIC语言)。对于初学者也许很容易理解,也不能说错,但是造成的误解将导致以后的程序编写
地千疮百孔。
上面的解释隐含了一个意思——变量是一块内存。这是严重错误的!如果变量是一块内存,那么C++中著名的引用类型将被弃置荒野。变量实际并不是一块内存,只是一个映
射元素,这是致关重要的。
-----------以上内容引自《C++从零开始》
数据类型
四类八种: 整型四种 浮点两种 字符型和布尔型
各种基本数据类型的存储空间,但这实际上只是java概念上一种约束,
在实际的计算机存储中,如在32位机器中,char型也是以32bit存储的。
关于浮点数
数学中的浮点数时连续的,如在[0,1]之间有无穷个浮点数。
但是在计算机内部不可能逐个表现他们,只能尽可能地模拟它们。
就是说在计算机中,它们是离散的。float类型使用32bit存储,可以最多表示232个浮点数。
类型转换、补码的相关知识
如下代码
byte b1 = 90;
byte b2 = 68;
byte b = (byte)(b1 + b2);
System.out.println(b);
这段代码输出的结果是 -100.
下面是非常详细的解释:
从第3行开始
对b1 + b2进行运算的时候,b1和b2首先自动转换为int类型的90和68,
相加得到int类型的156.
那么156转换为二进制后,理论上在计算机内的表示是(实际上可能略有差别)
00000000 00000000 00000000 10011100
然后156将被强制转换为byte类型:抛弃前面三个字节,只剩10011100.
java中整型都是有符号的,即最高位表示符号位。
10011100的最高为是1,表示它是个负数。
在java中,负数以补码存储。那么10011100是负多少呢?
首先要了解原码与补码的关系。原码和补码的相互转换都是通过求反加1。
因此要求10011100的原码,符号位1不变,尾数求反得到11100011,加1得到11100100,
最高为1是符号位,后7位转换为十进制为100,所以真值为-100.
Infinity
写出下列代码输出结果
double d= 1e300;
float f = (float)d;
System.out.println(f);
输出:Infinity.
double向 float转换时,如果超出float的取值范围则输出Infinity
数据类类型的存储
与取值范围大小没有必然关系
float 占用4个字节,long占用8个字节,但是float表示数值的范围比long大得多。
方法的本质
是复用性。
面向对象设计思想
从现实世界中客观存在的事物出发来构造软件系统,并在系统的构造中尽可能运用人类的自然思维方式。
面向对象更加强调运用人类日常生活中经常使用的思维方法与原则,如抽象、分类、继承、聚合、多态等等。
对象和类
对象是用计算机语言对问题域中事物的描述。对象通过属性(attribute)和方法(method)来分别对应事物所具有的静态属性和动态属性。
类是用来描述同类对象的抽象化了的概念。类中定义了这一类对象应具有的静态属性和动态属性。
类可以看成一类对象的模板,对象可以看成类的一个具体实例。
类之间的关系
- uses-a 一个类的方法操纵另一个类的对象叫做dependence (依赖)
- has-a 一个对象包含另一个类的对象叫做aggregation (聚合)
- extends 继承
- implements 实现
- polymorphism 多态
面向对象程序设计
首先考虑几个类几个对象;
然后设计类的属性;
最后设计类之间的关系。
让合适的方法出现在合适的类中
各种0的区分
在ASCII表中第一个条目是null,值是0;
后面还有一个条目是字符‘0’,值是48;
前者有以下几种叫法:务必搞清楚它们是同一种东西
null零
二进制零
ASCII零
\0(读作反斜杠零)
字符串结束符
成员变量
Java中没有全局变量 应该叫成员变量 (表述要严谨)
成员变量可以不初始化,局部变量必须初始化
成员变量不初始化时,有其默认值
char型的默认值
其中char型的默认值值得注意
准确的说char型的默认值是一个不可输出的字符,应该记作 ’\u0000’ 表示是UTF-16形式的,
而这个值正是上面所提到的null零、二进制零、ASCII零、反斜杠零。
有些资料上面把char类型的默认值直接写作0是不准确的。
尽管char在和整型运算时,首先转化为整型数字0. (也就是’\u0000’这个字符转化为数值0.)
UTF-16字符写代码
貌似很无聊。。。Java代码中的字符都可以用UTF-16形式的字符来表示
比如 public static void main(String[] args)完全可以写作
public staticvoid main(String\u005B\u005D args)
如果你高兴,还可以写作
public stativvoid main\u0028String\u005B\u005D args\u0029
方法重载
方法重载指一个类中可以定义多个方法,这些方法方法名相同,但参数形式有所不同。
调用时根据不同的参数列表选择对应的方法。首先进行“精确匹配”,如果找不到则进行“自动类型转换匹配”。
java源文件有最多一个public类
在一个java源文件中只能由一个public类。了解即可。
深入分析可以参考http://blog.csdn.net/bareheadzzq/article/details/6562211
public static void
调用main()的是java虚拟机。public 是为了让虚拟机看到。static表示调用main()不需要实例,虚拟机本来就存在,void表示没有返回值。
深入分析可以参考我的转载日志
http://blog.csdn.net/i10mg/article/details/8728728
内存管理
也有其他的划分方法,下面的相对简单一些
内存基本上可以划分为四个区域:stack、heap、data segment、code segment.
局部变量,包括形式参数存储在stack,用完后销毁;
new出来的对象存储在heap中,由java的垃圾回收机制自动回收。
静态变量和字符串常量存储在data segment.
upcasting 和 downcasting
父类型的引用指向子类型对象这就是upcasting
一个对象引用声明为父类型,却实例化了一个子类型的对象。
这时候通过该对象引用只能看到父类的属性,看不到子类自有的属性。
如想要看到子类自有的属性,需要downcasting. 即把这个引用强制转换为子类型。
这种转换实际上是转换了引用的视角。
当父类型引用指向子类型对象时,通过引用调用方法时,会调用子类型重写的方法,这就是所谓的overiding.
动态绑定
在执行期间,而非编译期间判断引用指向的对象的实际类型,根据其实际类型调用其相应的方法。
多态的条件
有继承;
有重写 ;
父类引用指向子类对象
final 的意义
不被改变,
不被继承,
不被重写
class的访问修饰符
能够修饰class的权限修饰符只有public 和没有(friendly)
修饰符可以有public, abstract, final和(friendly)
内部类看作成员,所以有四个权限修饰符
Math.round()
Math.round(-11.5)的值是-11.
round()表示先加0.5,再向下取整。
"深入"二维数组
用Arrays.deepToString(arr)方法
没有main()的Hello World
这个程序展示了不同过main()来打印Hello World的技巧。
把打印语句放到static块中,在出错之前用System.exit(0)退出
public class NoMain {
static {
System.out.println("Hello World");
System.exit(0);
}
}
fianl的类
如果将一个类声明为final, 那么只有其中的方法自动成为final, 而不包括域
抽象类
一个类即使不含抽象方法,也可声明为抽象类。抽象类变量可以引用非抽象类及其子类的对象。
异常
异常 java中异常可分为3类。
一类是Error,只能宕机了。如StackFlowError。
另一类是运行时异常。如空指针异常,数组下标越界。真正需要我们关心的是这一类。
最后一类是需要用户必须用try catch包围才可以的异常。方法注明了会抛出哪类异常,调用方法的时候必须用try,catch来捕捉异常,或者再次抛出给下一个调用它的对象。
外篇
递归列出文件目录
关键之处在于level参数,控制缩进量按照层次递增
private static void tree(File file, int level) { File[]members = file.listFiles(); // initial value of indent Strings = ""; for (int i = 0; i < level; i++) { // indent increases s+= " "; } for (int i = 0; i < members.length; i++) { System.out.println(s +members[i].getName()); if(members[i].isDirectory()) { // using recursion tree(members[i],level + 1); } } }
递归删除目录
private static void delDirs(File file) { // 如果是文件则直接删除 if (file.isFile()) { file.delete(); } else if (file.isDirectory()) { // 如果不是文件则列出子目录 File[]members = file.listFiles(); for (File member : members) { // 递归调用自身(recursion) delDirs(members[i]); } // 最后删除自身 file.delete(); } }
选号小程序
// numbers need to draw int k = 6; // numbers can draw int n = 49; // fill an array with 1 to 49 int[] numbers =newint[n]; for (int i = 0; i < numbers.length; i++) { numbers[i]= i + 1; } // draw k numbers and put them into a second array int[] result =newint[k]; for (int i = 0; i < result.length; i++) { // make a random index between 0 and n-1 int r = (int)((Math.random()* n)); // pick the element at the random location result[i]= numbers[r]; // move the last element into the random location numbers[r]= numbers[n - 1]; // random index's value span decrease by 1 for each timeit loops n--; } // sort and print the result array Arrays.sort(result); System.out.println("Bet the following combination, it'll make yourich!"); for (int r : result) { System.out.println(r); }
程序的关键之处在于产生result数组的for循环。首先产生一个0到48之间的值作为index,把这个index对应的值取出放到result中。然后把源数组最后一个元素移到被取走的元素位置上。这样保证不会取到重复的结果。
死锁Demo
public class Resource { String resourceName; public Resource(String name) { this.resourceName = name; } }
public class MyThread implements Runnable { Resource resourceA; Resource resourceB; Thread th; public MyThread(Resource A, Resource B,String name) { resourceA = A; resourceB = B; th = newThread(this); th.setName(name); } @Override public void run() { synchronized (resourceA) { System.out.println(th.getName() +" got " +resourceA.resourceName); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(th.getName() +" is waiting for " +resourceB.resourceName +" to be released."); synchronized (resourceB) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
public class DeadLockTest { public static void main(String[] args) { Resource resource1 = new Resource("Resource1"); Resource resource2 = new Resource("Resource2"); MyThread myThread1 = new MyThread(resource1,resource2,"thread1"); MyThread myThread2 = new MyThread(resource2,resource1,"thread2"); myThread1.th.start(); myThread2.th.start(); } }
运行结果:(程序未结束)
thread2 got Resource2
thread1 got Resource1
thread1 is waiting for Resource2 to be released.
thread2 is waiting for Resource1 to be released.