Java 基础知识点汇总
(1)overload:重载
在一个类定义中,可以编写几个同名的方法,但是只要它们的签名参数列表不同,Java就会将它们看做唯一的方法。简单的说,一个类中的方法与另一个方法同名,但是参数表不同,这种方法称之为重载方法。
(2)try···catch···finally的问题
程序中如果遇到return,则finally块先被执行,然后再执行retrun,而finally块后面的语句将不被执行。如果遇到System.exit(1),则finally块及其后的语句都不执行,整个程序退出,还执行什么呀。
(3)break的使用
public class Test002{
public void go(){
String o = "";
z:
for(int x = 0; x < 3; x++) {
for(int y = 0; y < 2; y++) {
if (x == 1) break;
if (x == 2 && y==1) break z;
o = o + x + y;
}
}
System.out.print(o);
}
public static void main(String[] args) {
Test002 t1 = new Test002();
t1.go();
}
}
break只能跳出当前循环,要想跳到指定的外围循环需用标记 z(是标记最外面的for循环),break z;表示跳出外围for循环
(4)编译错误与运行错误
编译错误,一般指语法上的错误,比如 int a = "abcdef";String a = 1234; int i = new object();
运行错误,一般指程序上的bug,比如你写的,d怎么转成double
(5)逻辑运算符
用于连接布尔型表达式。
在Java中不同于数学的逻辑表达 3<X<5 ,java 中应该写成 x>3 & x<5
"&" 和"&&" 的区别;
单个"&",左边无论真假,右边都进行计算。
双个"&",左边为假,右边不进行计算。
"|"和"||" 的区别;
单个"|",左边无论真假,右边都进行计算。
双个"|",左边为真,右边不进行计算。
"|"和"&" 做位运算
二进制进行|位运算,只有0|0时候为0 ,其他为1,比如6|5 先把6和5换成二进制。6的二进制为110,5的二进制为101; 那么用位运算0|0得0,只要有1得1,算出为111,111换成十进制为7,所有6|5等于7 二进制进行&位运算,只有1&1得1,其他得0,同理可得6&5算出二进制得100,100换成十进制为4
"^"异或运算和"~"反码
任何相同二进制异或运算都得0,1^1=0,0^0=0,不相同的异或运算都得1,1^0=1,0^1=1。同理6^5的011 那么011的十进制是3。提示一个数异或两次变回本身。比如6^5^5的 110,101,101。先得011,再得110所有又变回原来的数值, 此技巧可以用来转换数值,不需要第3个变量即可两个变量值互转。
反码:二进制进行反码计算时。1变成0,0变成1。举例子~6,6的二进制是110在机器上补足32位在110之前补码0。 那么反码为111.....001,此时的反码减去1得111.....111000,那么原反码减去相减的码得到0000....000111此时的十进制为-7 那么加上原来减去的1得到-7+1就是~6了。(这个我也不是很懂。大牛们指正下)
"<<"左移">>"右移">>>"无符号右移
左移:空缺补0,被移除的高位丢弃,空缺位补0,比如32位二进制 0000....00011左移3位变成了0...00011xxx,则xxx补足000即可。 规律左移是基数乘以2的移位幂次方,比如3<<2则是3*2*2也就是3乘以2的2次幂。 右移:最高位是什么就补什么。高位是1补1,是0就补0.,规律左移是基数除以2的移位幂次方,比如3>>1则是3/2也就是3除以2的1次幂。 无符号右移:无论高位是0还是1都补0
(6)"++"运算符
j = ++i;//先自加再赋值
j = i++;//先赋值再自加
(7)Arrays.binarySearch(数组,元素);
二分搜索法,使用之前要对数组中的元素进行排序(Arrays.sort(数组))。返回数值为-(插入点)- 1。插入点:重新从0排列之后的顺序
(8)子类重写父类的方法应注意的问题
若想实现一个合格重写方法,而不是重载,那么必须同时满足下面的要求!
重写规则之一:重写方法不能比被重写方法限制有更严格的访问级别。
严格程度:private>普通>protected>public (但是可以更广泛,比如父类方法是包访问权限,子类的重写方法是public访问权限。) 比如:Object类有个toString()方法, 开始重写这个方法的时候我们总容易忘记public修饰符,编译器当然不会放过任何教训我们 的机会。 出错的原因就是:没有加任何访问修饰符的方法具有包访问权限,包访问权限比public当然要严格了,所以编译器会报错的。
重写规则之二: 参数列表必须与被重写方法的相同。
重写有个孪生的弟弟叫重载,也就是后面要出场的。 如果子类方法的参数与父类对应的方法不同,那么就是你认错人了,那是重载,不是重写。
重写规则之三:返回类型必须与被重写方法的返回类型相同。
父类方法A:void eat(){} 子类方法B:int eat(){} 两者虽然参数相同,可是返回类型不同,所以不是重写。 父类方法A:int eat(){} 子类方法B:long eat(){} 返回类型虽然兼容父类,但是不同就是不同,所以不是重写。
重写规则之四:重写方法不能抛出新的异常或者比被重写方法声明的检查异常更广的检查异常。
但是可以抛出更少,更有限或者不抛出异常。 注意:这种限制只是针对检查异常,至于运行时异常RuntimeException及其子类不再这个限制之中。
重写规则之五: 不能重写被标识为final的方法。
重写规则之六:如果一个方法不能被继承,则不能重写它。
//如private方法,比较典型的就是父类的private方法。下例会产生一个有趣的现象。
public class Test {
public static void main (String[] args) {
//Animal h = new Horse();
Horse h = new Horse();
h.eat();
}
}
class Animal {
private void eat(){
System.out.println ("Animal is eating.");
}
}
class Horse extends Animal{
public void eat(){
System.out.println ("Horse is eating.");
}
}
这段代码是能通过编译的。表面上看来违反了第六条规则,但实际上那是一点巧合。Animal类的eat()方法不能被继承,因此Horse类中的 eat()方法是一个全新
的方法,不是重写也不是重载,只是一个只属于Horse类的全新的方法!这点让很多人迷惑了,但是也不是那么难以理解。
main()方法如果是这样:
Animal h = new Horse();
//Horse h = new Horse();
h.eat();
编译器会报错,为什么呢?Horse类的eat()方法是public的啊!应该可以调用啊!请牢记,多态只看父类引用的方法,而不看子类对象的方法!
重写规则之七:子类不能用 静态方法 重写 父类的非静态方法
编绎无法通过this static method cannot hide the instance mehtod from
class A {
protected int method1(int a, int b) {
return 0;
}
}
public class Test1 extends A {
private int method1(int a, long b) {
return 0;
}
//this static method cannot hide the instance mehtod from A
static public int method1(int a, int b) {
return 0;
}
}
重写规则之八:子类不能用 非静态方法 重写 父类的静态方法
编绎报错:this instance method cannot override the static mehtod from A
class A {
protected static int method1(int a, int b) {
return 0;
}
}
public class Test1 extends A {
private int method1(int a, long b) {
return 0;
}
//this static method cannot hide the instance mehtod from A
//this instance method cannot override the static mehtod from A
public int method1(int a, int b) {
return 0;
}
}
(9)构造函数调用构造函数
题目如下:问下列代码的打印结果为0吗?
#include <stdlib.h>
#include <iostream>
using namespace std;
struct CLS
{
int m_i;
CLS( int i ) : m_i(i){}
CLS()
{
CLS(0);
}
};
int main()
{
CLS obj;
cout << obj.m_i << endl;
system("PAUSE");
return 0;
}
打印结果是不定的,不一定为0
代码奇怪的地方在于构造函数中调用了自己的另一个构造函数
我们知道,当定义一个对象时,会按顺序做2件事情:
1)分配好内存(非静态数据成员是未初始化的)
2)调用构造函数(构造函数的本意就是初始化非静态数据成员)
显然上面代码中,CLS obj;这里已经为obj分配了内存,然后调用默认构造函数,但是默认构造函数还未执行完,却调用了另一个构造函数,这样相当于产生了一个匿名的临时CLS对象,它调用CLS(int)构造函数,将这个匿名临时对象自己的数据成员m_i初始化为0;但是obj的数据成员并没有得到初始化。于是obj的m_i是未初始化的,因此其值也是不确定的
从这里,我们归纳如下:
1)在c++里,由于构造函数允许有默认参数,使得这种构造函数调用构造函数来重用代码的需求大为减少
2)如果仅仅为了一个构造函数重用另一个构造函数的代码,那么完全可以把构造函数中的公共部分抽取出来定义一个成员函数(推荐为private),然后在每个需要这个代码的构造函数中调用该函数即可
3)偶尔我们还是希望在类的构造函数里调用另一个构造函数,可以按下面方式做:
在构造函数里调用另一个构造函数的关键是让第二个构造函数在第一次分配好的内存上执行,而不是分配新的内存,这个可以用标准库的placement new做到:
先看看标准库中placement new的定义
inline void *__cdecl operator new(size_t, void *_P)
{
return (_P);
}
//可见没有分配新的内存。
//正确的方式:
struct CLS
{
int m_i;
CLS( int i ) : m_i(i){}
CLS()
{
new (this)CLS(0);
}
};
另: 若构造函数调用自身,则会出现无限递归调用,是不允许的
(10)Java 关键字abstract详解
abstract 关键字可以修饰类或方法。
abstract 类可以扩展(增加子类),但不能直接实例化。
abstract 方法不在声明它的类中实现,但必须在某个子类中重写。
示例
public abstract class MyClass
{
}
public abstract String myMethod();
注:采用 abstract 方法的类本来就是抽象类,并且必须声明为 abstract。
abstract 类不能实例化。
仅当 abstract 类的子类实现其超类的所有 abstract 方法时,才能实例化 abstract 类的子类。这种类称为具体类,以区别于 abstract 类。
如果 abstract 类的子类没有实现其超类的所有 abstract 方法,该子类也是 abstract 类。
abstract 关键字不能应用于 static、private 或 final 方法,因为这些方法不能被重写,因此,不能在子类中实现。
final 类的方法都不能是 abstract,因为 final 类不能有子类。
(11)interface implement extends
1.interface的作用是让使用inerface的人不知道它是怎么实现的,但调用这结方法就好使。就像是收歀台,它的方法就是收钱。你把钱给它,它给你零钱。
你不用管它用的是电脑还是算盘。它也不想让你知道。反正功能实现了。
2.当你在使用inerface时,一定是用的implements里的方法,而且经常这么用。List l = new ArrayList() ; List是接口,ArrayList是它的实现。
这后还很有可能有个方法是这样的 public void m1(List l){...}.这里写List是让这个方法更通用些,只要是List 的实现就能用。就好比这个款台,台子是一样的,但钱可以不一样。可是美元,也可以是元宝。但它们都是“钱”的实现,“钱”本身没有意义。
3.implements 和 extends 。implements是向老师学习,我可以拜很多的人为师,从而我就有了这些人的本领,但我还是我,不过本事大了些;extends 就是认爸爸。一个人就一个爸,同样我有了爸爸的本领,你可以替你爸去从军,但不能替别的老头子,我也不能再认别的爸了,但可以认老师,几个都没关系。这之间也许还有别的区别,不过我还没遇到过。
4.还有一些,我也说不来了。我还分不太清什么时候用接口或是抽象类,不过我用接口的情况多些。有人对我说,最好是一个类对应一个接口,我还不理解它的意义有多大,不过大家都是这么写的。换句话说,一个类好不要凭空就出来,最差也要继承个抽象类。这都是书本上的,记住了,但没什么感觉。
(12)父类对象情况下不能转化为子类的对象,特殊情况如下
Father f = new Son();
Son s = (Son)f;
(12)引用类型和基本类型,常量池,栈和堆,内存释放,js性质
基本类型有8个,除此以外都是引用类型;
常量池范围:-128~127,在堆中
基本类型(比如int)是把常量取出来,把值放到栈里面
引用类型(比如integer),如果值在常量池中就指向常量池,如果不在就在堆里面new一个值,然后将栈里面的值指向堆里new的值。
常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。
它包括了关于类、方法、接口等中的常量,也包括字符串常量。
String是不可变的,比如说:String str = ”kv”+”ill”+” “+”ans”;
就是有4个字符串常量,首先”kv”和”ill”生成了”kvill”存在内存中,然后”kvill”又和” “ 生成 ”kvill “存在内存中,最后又和生成了”kvill ans”;并把这个字符串的地址赋给了str,就是因为String的“不可变”产生了很多临时变量,这也就是为什么建议用StringBuffer的原因了,因为StringBuffer是可改变的。
java讲究不要让内存空间无法释放,比如action传一个user对象到service层,如果在service层new一个user2=user的话,user置空,user2依然指向栈,不能够释放。
(13)在方法、类中定义变量须知
定义一个变量,在方法中必须赋初始值,否则编译报错;
注:在类中定义变量,如果没有赋初值,则默认false或者0。