说明:该文是本人回忆自己学习Java过程中产生的理解和总结,不能保证正确性和条理性。如果有不正确的地方欢迎提出,万分感谢。
Java的起源和历史,这些文字性的东西,我就不详细介绍了,这些基本每本这方面的书都会有。
先说说Java这个语言,源于C++但脱胎于C++,引入了很多很有意思的特性。
Java的三大特征
1.封装
说到封装,不得不提一个概念:万物皆对象。通过合理的抽象获取一类事物的共有特征,将这些特征包装成一个类。而与之相对的对象,则是表示这个类表示的事物中的某个个体。换句话就是,类表示一种,对象表示一个。
而封装就是将抽象得到的数据和功能进行再包装的过程。当然,这个再包装的过程并不是简简单单的过程。因为封装的目的是简化开发和增加安全性,所以封装需要遵循以下几个规则:
1.对外隐藏实现细节。也就是说,方法的具体实现调用方不可知。
2.隐藏属性内容。出于安全的考虑,对象的属性必须要经过一定权限控制。
3.用户不能像C/C++中通过指针操作对象,即用户不能直接访问内存空间。
因为封装这种特性,Java的代码安全性从起点就比其他的面向过程要安全。
现在,详细说说封装。
1.1指针
指针在C/C++中可以说是备受推崇。因为有了指针程序员可以灵活地操纵内存,使程序更有效率。但是,指针也是C/C++中一直被人所抨击的地方。因为指针的应用极其考验程序员的功底和开发习惯。指针在一些新手和不良习惯的程序员手里成为了造成内存溢出、程序崩溃的罪魁祸首。当然,这里的新手并不是那种刚刚入门的人,而是那种学了一部分就感觉自己已经精通了的人。
而Java彻底抛弃了这个让人又爱又恨的特性。通过封装,将对象的引用传递给用户,用户通过引用操作对象。这样用户就不能操作内存了。
但是这样也有一定程度的弊端,那就是程序员无法主动干涉内存释放。虽然JVM接管了内存释放,但是当用户同时多次申请大量内存的话就会发生堆栈溢出,也就是OutOfMemoryError。我想很多人都遇到过。对于这个错误,暂时不清楚解决方法。目前我只了解一个可以一定程度上的缓解方法,那就是在定义类的时候重写finalize()方法。关于这个方法后面会介绍。
1.2类、属性、方法
前面提到类是对一类事物的抽象。所以可以了解到,类 是抽象的。那么,类到底抽象了哪些东西呢?类抽象了事物的特性(属性),事物应该具有的功能(方法)。
属性:事物的特征、特性,或者说是概念定义。就像矩形的长、宽等这些属性,我们定义了长,然后再定义宽,就定义了一个矩形。所以,当我们定义了类的所有属性,我们就定义了一个可以区分的对象。
方法:事物具有的功能,或者与其有极大关联的功能。还是矩形这个例子,矩形的计算周长、计算面积,这些方法并不算是矩形具有的功能,但是因为这些功能的主体不明而且又与矩形关联巨大,所以,这些方法就会抽象到矩形类里面。
类、属性、方法,这些概念都是抽象而来的。那么,如何选择抽象程度呢?个人经历和一些网上的前辈给出的建议是:根据实际业务需求。比如说,人,如果抽象成类,将会具备很多属性和功能。如果,应用到身份证管理系统中的话,那么人具备的吃饭、体重这些再抽象就不合适了。(因为个人语言能力所限,例子可能不是很恰当,希望能够表达出意味来)
1.3访问权限控制
说起封装,就不得不提访问权限控制了。因为正是访问权限控制完善了封装。
Java的访问权限控制简略介绍如下:
同一个类 | 同一个Java文件中其他类 | 同一个包 | 子类 | 其他类 | 备注 | |
private | 支持 | 不支持 | 不支持 | 不支持 | 不支持 | 接口不支持private。内部类可以访问其外部类的private属性、方法 |
protected | 支持 | 支持 | 支持 | 支持 | 不支持 | |
default | 支持 | 支持 | 支持 | 不支持 | 不支持 | default作为访问权限关键字时,可以写,也可以不写。 |
public | 支持 | 支持 | 支持 | 支持 | 支持 | public 控制的方法和属性是对全局公开的。 |
当然,上表仅仅是一种简略介绍。
private:如果我们使用private控制时,就表示我们不想要其他其他用户访问它,它是对类内部公开。
default:也称为包内公开,也就是说default控制的方法、属性这些是对包公开。
protected:如果我们希望将某个方法交给子类去实现的话,那么就要用protected作为访问权限控制。
public:全局公开,这个是对全局公开的。也就是说,只要在你知道有这个类,那么就可以访问。
2.继承
根据《Java编程思想》中对继承的说明,继承就是“A is B”。我认为,这个很好的说明了继承的特性。只有满足“A is B”,才能说A继承了B。
与C++中不同的地方,Java只支持单继承。换句话就是一个父类有多个子类,而一个子类只有一个父类。那么,如果我们需要像C++中“继承多个类”呢?这就交给了接口。
这里插一句,继承是具有传递性的,也就是说“A is B,C is A”那么“C is B”。
Java中继承的关键字是:extends。
例子代码如下:
public class B{
private int ival;
private String test;
protected void setIval(int ival){
this.ival = ival;
}
private void init(){
ival = 10;
test = "Hello World!"
}
public void test(){
System.out.println("test : "+test);
}
}
public class A extends B{
}
那么,A类可以访问它父类B的哪些内容呢?
protected void setIval(int ival);
public void test();
有且仅有这些A类可以访问。似乎有些少啊。虽然看上去是这样的,但其实A类可以拥有自己的属性和方法,同时A类可以重写父类中它可以访问的方法,甚至可以修改protected控制的方法访问权限,当然了不能降低其访问控制范围,换句话说,就是可以将其公开。
同时,在实际应用中,子类甚至可以代替父类对象。
这里提到了重写,所谓的重写呢,个人理解就是子类写了一个和父类具有相同参数列表、相同方法名、相同返回类型、相同或大于父类访问控制权限的方法。这里有一个前提,就是子类要重写的这个方法不能是private的。至于default,我只能说这个是对包公开的。也就是在一个包里的子类可以重写,和父类不在一个包的子类则不能重写。
3.多态
多态分为运行时多态和编译时多态。运行时多态是动态多态,其具体引用的对象在运行时才能确定。编译时多态是静态多态,在编译时就可以确定对象使用的形式。这里介绍不是很详细,具体可以参照http://blog.youkuaiyun.com/tophot115/article/details/3016033。
个人认为多态就是对重写、重载以及继承、实现的综合应用。
以上简略的介绍了Java的三大特性。由于个人水平所限,无法表述其亮点的百中之一。在这里,我推荐一本《Java编程思想(Thinking in Java)》。其高明之处,我就不用自己浅薄的语言描述了。
数据类型
Java的数据类型大体分为基本数据类型和引用数据类型。
1.基本数据类型
1.1 数字型
字节型:byte(Byte) 其取值范围是:-128~127,数据宽度:8位
短整型:short(Short)其取值范围是:-32768~32767 数据宽度是:16位
整型:int(Integer)其取值范围是:-2147483648~2147483648 数据宽度是:32位
长整型:long(Long)其取值范围是:-9223372036854775808~9223372036854775807 数据宽度是:64位
浮点型:float(FLoat) 其取值范围是:1.4E-45~3.4028235E38
双精度:double(Double) 其取值范围是:4.9E-324~1.7976931348623157E308
关于浮点型和双精度,具体的长度我不太了解了。
现在,具体说一下这几个数字型的数据类型。括号里面的是对应的Java封装类,里面有一些比较使用的方法。这里就不具体的一一阐述。这里说一下大概的应用规则:
1.byte 因为其比较窄的存储范围,如果用来做多次数学算术的话,很容易发生溢出,造成数据丢失的现象。所以,平时一般用来作为对象的状态表示。当然也可以做为小数值的计算,不过不推荐。
2.short 大型项目中在实际中很少使用到,可能嵌入式中会用到。
3.int 可以说是最常用的整数类型。
4.long 用来表示比较大的整数类型。在声明的时候,建议在值后面加上‘L’或‘l’(小写的L)。这样有益于增强代码的阅读性。(印象中,如果不加L编译器会报错,但MyEclipse对此没有任何反应)。
5.float和double,都用来表示小数,如果不特殊注明,Java默认该小数是double类型的。
1.2非数字型
布尔型: boolean(Boolean) 取值true,false
字符型:char(Character)表示一个字符,这个字符可以是类似中文的宽字符,也可以是类似英文数字的短字符。
与C++不同的是,Java的布尔型关键字采用的是:boolean,而不是bool。同时,还有一个区别就是,boolean类型的变量不能直接转换为数字型变量。而C++的bool型,可以完成bool和int之间的转换。
Java在现有流行的JDK版本中,char类型都采用了UNICODE编码,也就是说,现有语言的单个字符都能用char类型来表示(没有试验过,这是UNICODE的定义的,Java的char采用了UNICODE编码,我想应该也可以吧)。
2.引用数据类型
字符串型:String
日期型:Date
大整数:BigInteger
大精度小数:BigDecimal
其他类型:Class类
引用数据类型,说白了也就是我们在编写的时候所写的Java类。这就是我们常说的引用数据类型。
方法的重写、重载
1.重写(Override)
重写,一般指子类重写父类中子类可以访问的方法。
重写需要满足的条件是:
相同的方法名,相同的返回类型,相同的参数列表,相同或者更加宽的访问权限控制。
为什么需要重写呢?
我想我们平时肯定会遇到这么一个问题,有这么一个方法,父类和子类有着不同的实现。就比如说吃饭这个动作,父亲习惯拿筷子,而他的小儿子却喜欢用勺子。那么,如果没有重写的话,我们应该怎样表示呢?难道说像这样定义?
class Father{
public void fatherEat(){
System.out.println("我是父亲,我习惯用筷子");
}
}
class Son{
public void sonEat(){
System.out.println("我是儿子,我喜欢用勺子");
}
}
如果你喜欢这样,那么当类多了之后,继承关系复杂之后,你会发现你的方法名字可能会很长,而且不具备良好的阅读性。
那么使用了重写之后,又会是怎样的?
class Father{
public void eat(){
System.out.println("我是父亲,我习惯用筷子");
}
}
class Son{
public void eat(){
System.out.println("我是儿子,我喜欢用勺子");
}
}
那么,这样你就会很轻易的发现,无论父类有多少个子类,有多少个子类的子类,方法只有一个。
既然有了重写,我们在实际中应该怎样应用呢?
我们先来看看这两行代码的结果是怎样的:
Father son = new Son();
son.eat();
如果知道结果是输出
我是儿子,我喜欢用勺子
那么,你基本明白了重写是怎样用了。
因为继承的关系,我们在实际开发应用中,子类对象可以作为父类对象使用。但是,当子类对象作为父类对象使用的时候,就会产生一个问题,那就是这个子类对象只能调用父类所拥有的方法。那么,这个方法的具体实现是使用父类的呢,还是子类的呢?
这时候,如果子类重写了这个方法,将会调用子类对象自己重写的方法,如果没有则调用父类原有的。
这样就会实现了运行时多态这一特性。
重写也是对程序代码扩展性的一种支持。
2.重载(Overload)
重载的官方定义我不太清楚了。但是重载的实现需要满足一下条件:
相同的访问控制权限(一般都是public,因为需要被调用)、相同的方法名、不同的参数列表。
Java是通过参数列表的不同来区别方法的不同。
重载实现了编译时多态。