本文内容来自本人在学习Java过程中整理的笔记,供自己日后翻阅,也可以作为Java入门基础的读者一部分学习资料,有问题欢迎留言,知无不言。
1.当一个类有多个构造器时,一个构造器调用另外一个构造器,可以使用this。
class Post {
private String title;
private String content;
public Post(String title) // 第一个构造器
{
this.title = title;
}
public Post(String title, String content)// 第二个构造器
{
this(title);
this.content = content;
}
}
在第二个构造器中,this(title);这一行代码表示调用第一个构造器。 在构造器比较复杂时,这种方式可以让代码更加简洁。
2.我们也可以定义有参数的构造器,即构造器也是可以重载的。比如:
class Post {
private String title;
private String content;
public Post(String title, String content)
{
this.title = title;
this.content = content;
}
}
这里构造函数具有两个参数。this表示的是当前对象,表示将参数title和content赋给自己对应的成员变量。如果自行定义了构造器,则编译器就不再会为我们生成默认构造器,针对以上Post代码,通过Post post = new Post();来创建Post对象将会产生编译错误。
3.构造器类似于普通方法,但是有两个特殊的地方:方法名称必须和类名相同,不允许定义返回类型。如果你没有定义任何构造器,则编译器会自动帮你生成一个构造器,称之默认构造器。 默认构造器不包含任何参数,会自动调用其父类的无参数构造器。回顾此前Post类的定义:
class Post {
private String title;
private String content;
}
因为没有显式定义构造器,所以编译器会帮我们生成默认构造器。因此我们可以使用new操作符来调用默认构造器。例如: Post post = new Post();构造器也可以使用public和priavate 修饰。如果使用private修饰,则外部不能通过new操作创建该类的实例。
4.如果一个方法定义了返回类型,在方法体内必须有return语句返回该类型的数据。return后面可以跟字面量、变量或者表达式。return也可以单独使用,不跟任何表达式,表示立即结束当前方法的执行。.return也表示无条件分支,它包含两种用途:(1)return后面跟一个值或者变量,指定一个方法的返回值(2)单独的return;语句,后面不跟任何值或者变量,表示退出当前方法比如我们可以定义一个方法来计算从1到100之和,然后将其返回:
public static int sumOfNumbers() {
int sum = 0;
for(int i = 0; i <= 100; i++)
{
sum += i;
}
return sum;
}
5.private:表示任何其他类不能直接访问该成员变量,只有该类自身可以访问
protected:表示只有该类自身及其子类可以访问该成员变量
public:表示任何类都可以直接访问该成员变量。
没有修饰:表示同一个包的类可以访问该成员变量
6.关于类和Java文件的关系:一般情况下,都是一个类一个.java文件如果一个.java文件里有多个类,只可能有一个public的类。而且文件名必须和public类同名如果文件里所有类都不是public的,那么文件名和任意一个类同名即可。
7.continue用于在循环控制结构中,让程序立刻跳转到下一次循环。在for循环中,continue语句使程序立即跳转到控制变量更新语句。在while或者do/while循环中,程序立即跳转到布尔表达式的判断语句。比如,我们计算从1到100的所有偶数之和,我们也可以通过加入continue语句来实现:
int sum = 0;
for(int i = 0; i <= 100; i++)
{
if (i % 2 == 1)
{
continue; // 如果是奇数,则立刻跳转到下一次循环,不做累加
}
sum += i;
}
8.Java利用for语句引入了更为简单的方式来进行Java数据及容器的操作。通过这种方式,我们可以不用引入额外的控制变量。以遍历数组为例: 一般的for循环:
String[] sentences = {"hello", "thank u", "thank u very much"};
for (int index = 0; index < sentences.length; index++)
{
System.out.println(sentences[index]);
}
//可以采用如下方式进行简化:
String[] sentences = {"hello", "thank u", "thank u very much"};
for (String sentence : sentences)
{
System.out.println(sentence);
}
9.'=='和'!='作为关系运算符只用来比较对象的引用。如果想比较两个对象实际内容是否相同,需要调用对象的equals()方法。比如判断一个字符串str的内容是否为"abcd",应该这样比较:
if (str.equals("abcd")) {
}
//下面这种方式是一种错误的方式:
if (str == "abcd") {
}
10.命名冲突即类同名的情况,想想一下同一个文件夹下是不可以有两个同名的文件的,不同文件夹下就不存在这个问题,包可以看做是存放Java类的不同文件夹。Java平台提供了很多方便我们编程的类,通常称之为类库(lib),也称之为应用编程接口(Application Programming Interface, API)。不仅仅是使用Java平台提供的类需要import,任何一个package中的类,如果需要访问另外一个package中的类,就需要import.
11.引用类型和基本类型的区别
int color = 0;
int speed = 100;
Car myCar = new Car(color, speed);
则内存状态如下:
与引用类型myCar不同,基本类型变量的值就是存储在栈中,作用域结束(比如main方法执行结束)则这些变量占据的栈内存会自动释放。
12.在外部(即其它类中)也可以访问一个类的非private属性,通过对象名.属性名的方式进行访问。例如将Car的color属性设置为public:
public class Car {
public int color;
// ...
}
如果我们定义一个Driver类,可以这样访问color属性:
public class Driver {
public static void main(String[] args) {
Car car = new Car();
car.color = 0xffffff; // 修改color属性的值
int color = car.color; // 访问color属性的值,将其赋给其他变量
System.out.println(car.color); // 将color作为参数,打印
}
}
13.如果自行定义了构造器,则编译器就不再会为我们生成默认构造器,针对以上Post代码,通过Post post = new Post();来创建Post对象将会产生编译错误,除非你显式地增加默认构造器:
class Post {
private String title;
private String content;
public Post() {
}
public Post(String title, String content) {
this.title = title;
this.content = content;
}
}
14.equals()与==的区别:如果两个字符串变量指向的字符序列内容完全一样,equals()返回true;如果两个字符串变量指向同一个对象,==返回true。
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(str1 == str2);
System.out.println(str1.equals(str2));
以上代码,第一个输出为false,第二个输出为true。
15.一个类的成员变量,一个函数中的参数,都具有一种数据类型,可以为基本数据类型(如int类型)或者引用类型(如Car类型)。假设我们想描述平面坐标的一个点,那么我们创建一个Point类,这个类要包含表示X坐标和Y坐标的成员变量:
public class IntPoint{
private int x;
private int y;
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
如果我们发现int类型描述平面坐标上的点精度不够,希望换做double类型,那么代码应该大部分是类似的,可是上面的代码我们却无法复用。我们只能再定义一个非常类似的DoublePoint重新定义为:
public class DoublePoint{
private double x;
private double y;
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
}
能否只定义一个类就能满足坐标既可能为int类型,也可能为double类型的情况呢?如果可以的话将可以让代码更加通用,减少大量的重复代码。答案是肯定的,这个时候你需要泛型。
在使用泛型时,我们可以把类型作为参数传入到泛型类中。类似于把参数传入到方法中一样。我们来实现一个通用的泛型Point类
public class Point<T> {
private T x;
private T y;
public T getX() {
return x;
}
public void setX(T x) {
this.x = x;
}
public T getY() {
return y;
}
public void setY(T y) {
this.y = y;
}
}
此时Point成为了一个泛型类,T是则是类型参数,T具体是什么类型那要看程序运行的时候我们传入什么类型给他。使用泛型类时,注意实际传入的类型参数不能是原生类型,必须是引用类型,因此如果希望传入int类型的话,那么需要传入int对应的包装类Interger。对应地,double类型则要传入包装类Double。
public class Test{
public static void main(String[] args){
// 坐标为int类型,把int类型的包装类Integer作为参数传入泛型类中
Point<Integer> point1 = new Point<Integer>();
point1.setX(1);
point1.setY(1);
// 坐标为double类型,把double类型的包装类Double作为参数传入泛型类中
Point<Double> point2 = new Point<Double>();
point2.setX(3.456);
point2.setY(4.789);
}
}
Point<Integer>等价于IntPoint类,而Point<Double>则等价于DoublePoint类。
再举一个例子,我们定义一个容器类Container,这个容器中可以存放各种类型的对象,可以使用泛型类实现这一特性。
public class Container<T> {
private T variable;
public Container () {
variable = null;
}
public Container (T variable) {
this.variable = variable;
}
public T get() {
return variable;
}
public void set(T variable) {
this.variable = variable;
}
public static void main(String[] args) {
Container<String> stringContainer = new Container<String>();
stringContainer.set("this is a string");
}
}
我们实例化Container对象时,只需设置它使用的类型,如:
Container<String> stringContainer = new Container<String>();
stringContainer.set("this is a string");
16.static的作用:大家都知道,我们可以基于一个类创建多个该类的对象,每个对象都拥有自己的成员,互相独立。然而在某些时候,我们更希望该类所有的对象共享同一个成员。此时就是 static 大显身手的时候了!Java 中被 static 修饰的成员称为静态成员或类成员。它属于整个类所有,而不是某个对象所有,即被类的所有对象所共享。静态成员可以使用类名直接访问,也可以使用对象名进行访问。当然,鉴于他作用的特殊性更推荐用类名访问。使用 static 可以修饰变量、方法和代码块。例如,我们在类中定义了一个 静态变量 hobby ,操作代码如下所示:
public class HelloWorld {
static String hobby="kat";
public static void main(String[] args) {
System.out.println("通过类名访问hobby:"+HelloWorld.hobby);
HelloWorld hello=new HelloWorld();
System.out.println("通过对象名访问hobby:"+hello.hobby);
hello.hobby="melo";
System.out.println("再次通过类名访问hobby:"+HelloWorld.hobby);//打印结果:melo
}
}
静态成员属于整个类,当系统第一次使用该类时,就会为其分配内存空间直到该类被卸载才会进行资源回收!
关于static,有一道笔试题,不妨一看。
class Test{
private int m;
public static void fun(){
//some code...
}
}
如何使成员变量m被函数fun()直接访问
A.将private int m改为protected int m
B.将private int m改为public int m
C.将private int m改为static int m
D.将private int m改为int m
本题选C
17.Java集合:集合类存放于java.util包中,java集合主要可以划分为四个部分,List、Set、Map、工具类(Iterator迭代器、Enumeration枚举类、Arrays和VCollections)。java的集合类主要由两个接口派生而来,Collection和Map。在导入Set和List类时,方法是import java.util.Set和import java.util.List。但是注意Map和Collection都是借口,不能像Set类和List一样类导入。该怎么导入呢?例如我们要导入下面Colletion接口图中的TreeSet这个类,或者Map接口HashMap类,可以这样导入
Map map=new HashMap();//import java.util.HashMap;和import java.util.Map;
Collection col=new TreeSet();//import java.util.Collection;和import java.util.TreeSet;
分别来看一下这两个接口的体系
Colletion接口
Map接口
这个是总的结构图
(1)Collection接口是集合类的根接口,Java中没有提供这个接口的直接的实现类。但是却让其被继承产生了两个接口,就是Set和List。Set中不能包含重复的元素。List是一个有序的集合,可以包含重复的元素,提供了按索引访问的方式。
(2)Map是Java.util包中的另一个接口,它和Collection接口没有关系,是相互独立的,但是都属于集合类的一部分。Map包含了key-value对。Map不能包含重复的key,但是可以包含相同的value。
(3)Iterator,所有的集合类,都实现了Iterator接口,这是一个用于遍历集合中元素的接口,主要包含以下三种方法:
1.hasNext()是否还有下一个元素。
2.next()返回下一个元素。
3.remove()删除当前元素。
(4)ArrayList和LinkedList在用法上没有区别,但是在功能上还是有区别的。LinkedList经常用在增删操作较多而查询操作很少的情况下,ArrayList则相反。
(5)Map集合,实现类:HashMap、Hashtable、LinkedHashMap和TreeMap
HashMap是最常用的Map,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是完全随机的。因为键对象不可以重复,所以HashMap最多只允许一条记录的键为Null,允许多条记录的值为Null,是非同步的。
Hashtable,Hashtable与HashMap类似,是HashMap的线程安全版,它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢,它继承自Dictionary类,不同的是它不允许记录的键或者值为null,同时效率较低。
LinkedHashMap保存了记录的插入顺序,在用Iteraor遍历LinkedHashMap时,先得到的记录肯定是先插入的,在遍历的时候会比HashMap慢,有HashMap的全部特性。
TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序(自然顺序),也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。不允许key值为空,非同步的。
有关集合,有一个例子特别好
package knowledgeAboutColletion;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class HashSetTest {
public static void main(String args[]){
/*1.Collection<String>:泛型接口,这里的泛型String,所以添加的元素只能是String类型
*2.ArrayList属于继承Collection接口的类,这个属于多态的向上转型
*3.list属于接口,它有五种方法,add、remove、isEmpty、iterator、size*/
Collection<String> list=new HashSet<>();//打印结果a,abc。Set集合不允许重复。
list.add("abc");//添加字符串
list.add("a");
list.add("abc");
/*Iterator迭代器,有三种方法
*next():获得序列的下一个元素
*hasNext():序列中是否还有元素
*remove():将迭代器新返回的元素删除*/
Iterator<String> it=list.iterator();
while(it.hasNext()){
String str=it.next();
System.out.println(str);
}
}
}
如果是List集合,则不一样,可以重复。
package knowledgeAboutColletion;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class HashSetTest {
public static void main(String args[]){
Collection<String> list=new LinkedList<>();//打印结果abc,a,abc。List集合可以重复。
list.add("abc");
list.add("a");
list.add("abc");
Iterator<String> it=list.iterator();
while(it.hasNext()){
String str=it.next();
System.out.println(str);
}
}
}
这里把ArrayList改成LinkedList效果差不多,两者的区别百度一下,会有很多文章,同理上面的TreeSet和HashSet。暂时不细分同一接口下不同继承类的差别。
如果上面的Collection没有泛型呢?
package knowledgeAboutColletion;
import java.util.Collection;
import java.util.Iterator;
import java.util.TreeSet;
public class HashSetTest {
public static void main(String args[]){
HashSetTest t=new HashSetTest();
Collection list=new HashSet<>();//打印结果a,abc,Set集合不允许重复
list.add("abc");
list.add("a");
list.add("abc");
list.add(t);//添加对象,由于之前Collection泛型String,所以不能添加对象t,但是可以添加t.toString
Iterator<String> it=list.iterator();
while(it.hasNext()){
String str=it.next();
System.out.println(str);
}
}
}
打印结果
a
abc
Exception in thread "main" java.lang.ClassCastException: knowledgeAboutColletion.HashSetTest cannot be cast to java.lang.String
at knowledgeAboutColletion.HashSetTest.main(HashSetTest.java:24)
对象t可以增加到集合中,但是无法打印出来。
Map集合的实例
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class HashSetTest {
public static void main(String args[]){
/*1.创建Map接口实例,这里泛型,并且用了两个泛型,这样泛型,后面put方法中填入的key-value只能是String类型
* 如果没有这个泛型,可以是任何类型的
2.Map接口的六大方法
(1)put(K key,V value):向集合中添加指定的key与value的映射关系
(2)containsKey
(3)containsValue
(4)get
(5)keySet:返回所有key对象形成的Set集合
(6)values:返回所有key对象形成的Collection集合*/
Map<String,String> map=new HashMap<>();
map.put("01", "jason");//put方法需要两个
map.put("02", "shu");
Set<String> set=map.keySet();
//iterator方法,Collection集合的方法,返回在此Collection的元素上进行迭代的迭代器
Iterator<String> it1=set.iterator();
System.out.println("key集合中的元素");
//整个过程,从Map中提取key→将key转换为Set集合→为Set集合创建迭代器(因为要遍历)
while(it1.hasNext()){
System.out.println(it1.next());
}
Collection<String> col=map.values();//解释如上
Iterator<String> it2=col.iterator();
System.out.println("values集合中的元素");
while(it2.hasNext()){
System.out.println(it2.next());
}
}
}
18.类的封装:封装是一种隐藏信息的技术,是将一个系统中的结构和行为通过类来划分的过程。即通过定义一组类,将特定的数据组合到某一个类中,形成一个整体,将该隐藏的数据进行保护,只对外暴露这些数据的访问的方法。封装代码有两个好处:(1)代码使用者无需考虑实现细节就能直接使用它,同时不用担心不可预料的副作用,别人不能随便修改内部结构(2)在外部接口保持不变的情况下,自己可以修改内部的实现。Java是通过访问控制关键字来实现的信息隐藏的,一共有三个关键字:public、protected和private。如果要隐藏一个类的成员变量,只要在该成员变量的前面加上private,这样外部就无法直接通过类的实例来访问这些成员变量了。
package com.tianmaying.domain;
public class Post {
int id;
int title;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
使用get和set方法是为了程序的封装,为了其它的类可以使用(设置和获取)该类的私有方法。为什么要用get和set方法而不是用直接用public呢?譬如写个基类Base,然后有人继承了Base:
public class Base {
public String[] data;
}
class Sub extends Base{
public void show(){
System.out.println(Arrays.toString(this.getData()));
}
}
可是有一天你发觉用ArrayList更好,你就把Base改成:
public class Base {
//public String[] data;
public ArrayList data;
}
然后所有Base的子类都无法编译,直接调用到子类data域的类也无法编译。回过头来想,如果当初Base的data域是private,由get/set来访问,那么你可以轻松的修改Base, 子类无需改动:
public class Base {
//private String[] data;
private ArrayList<String> data;
public String[] getData() {
return (String[]) data.toArray();
}
public void setData(String[] data) {
this.data = new ArrayList<String>(Arrays.asList(data));
}
}
顺便说一下,Eclipse有自动生成标准get/set函数的功能,所以生成get/set是很容易的事情,不要为了方便把成员都变成public。
19.类的继承:继承使用extends关键字,Java允许一个类仅能继承一个其它类,即一个类只能有一个父类,这个限制被称做单继承性。后面将会学到接口(interface)的概念,接口允许多继承。
class People{
String name;
int age;
int height;
void say(){
System.out.println("我的名字是 " + name + ",年龄是 " + age + ",身高是 " + height);
}
}
定义一个Teacher类来继承People类
class Teacher extends People{
String school; // 所在学校
String subject; // 学科
int seniority; // 教龄
void say(){// 覆盖 People 类中的 say() 方法
System.out.println("我叫" + name + ",在" + school + "教" + subject + ",有" + seniority + "年教龄");
}
void lecturing(){
System.out.println("我已经" + age + "岁了,依然站在讲台上讲课");
}
}
(1)name和age变量虽然没有在 Teacher 中定义,但是已在People中定义,可以直接拿来用。
(2)Teacher是People的子类,People是Teacher类的父类。
(3)子类可以覆盖父类的方法。
(4) 子类可以继承父类除private以外的所有的成员,不能继承private的方法和变量,这也是封装的原理。
(5)构造方法不能被继承。
20.super关键字:super 关键字与 this 类似,this 用来表示当前类的实例,super 用来表示父类。super 关键字的功能:
(1)调用父类中声明为private的变量。
(2)点取已经覆盖了的方法。
(3)作为方法名表示父类构造方法。
public class Demo{
public static void main(String[] args) {
Dog dog = new Dog();
dog.move();
}
}
class Animal{
private String desc = "Animals are human's good friends";
// 必须要声明一个 getter 方法
public String getDesc() {
return desc;
}
public void move(){
System.out.println("Animals can move");
}
}
class Dog extends Animal{
public void move(){
super.move(); // 调用父类的方法,super可以理解为父类对象
System.out.println("Dogs can walk and run");
// 通过 getter 方法调用父类隐藏变量
System.out.println("Please remember: " + super.getDesc());
}
}
打印结果:
Animals can move
Dogs can walk and run
Please remember: Animals are human's good friends
21.final关键字:一个变量可以声明为final,这样做的目的是阻止它的内容被修改。这意味着在声明final变量的时候,必须初始化它,通常情况下,我们会使用final来定义一些常量。
22.在面向对象的领域一切都是对象,所有的对象都是通过类来描述的。如果我们要定义的一个类没有足够的信息来描述一个具体的对象,还需要其他的具体类来支持,这个时候我们可以考虑使用抽象类。在类定义的前面增加abstract关键字,就表明一个类是抽象类。抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。
abstract关键字同样可以用来声明抽象方法,抽象方法只包含一个方法名,而没有方法体。抽象方法没有定义,方法名后面直接跟一个分号,而不是花括号。声明抽象方法会带来以下两个结果:(1)如果一个类包含抽象方法,那么该类必须是抽象类。(2)任何子类必须重写父类的抽象方法,否则就必须声明自身为抽象类。
一般情况下,我们将一个类声明为abstract的,是因为它包含了没有具体实现的抽象方法。比如说我们给Graph类增加一个求面积的方法area(),因为我们不知道图形的形状,我们是无法给出实现的,只能交给特定的子类去实现,这时我们只能将area()声明为abstract的,代码如下:
abstract class Graph {
String name;
public Graph(){}
public Graph(String name) {
this.name = name;
}
public void show() {
System.out.println("I'm a graph");
}
public abstract double area();
}
这时Rectangle类就必须给出area()方法的实现,否则它自己也必须用abstract修饰。
class Rectangle extends Graph{
public double area() {
return width * height;
}
}
23.接口:与抽象类类似的一个重要概念是接口。接口(Interface)是一组抽象方法的集合。接口中定义的方法没有方法体,它们以分号结束。接口也和抽象类一样,无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。编写接口和编写类的方式是大体上是类似的,一个接口可以有多个方法,代码保存在以接口命名且以.java结尾的文件中。接口使用interface关键字进行定义。比如我们定义一个Animal接口:
interface Animal {
void eat();
void sleep();
}
这个接口包含了两个抽象方法:eat()和sleep()。接口中的方法都是外部可访问的,因此我们可以不需要用public修饰。接口中也可以声明变量,一般是final和static类型的,要以常量来初始化,实现接口的类不能改变接口中的变量。比如我们可以在Animal接口增加一个成员变量TIMES_OF_EATING,表示动物每天吃饭的次数。接口访问权限有两种:public权限和默认权限,如果接口的访问权限是public的话,所有的方法和变量都是public。默认权限则同一个包内的类可以访问。一个接口能继承另一个接口,和类之间的继承方式比较相似。接口的继承使用extends关键字,子接口继承父接口的方法。比如我们可以定义TerrestrialAnimal接口,表示陆栖动物,它继承自Animal接口,同时还具有run()方法。
interface TerrestrialAnimal extends Animal {
void run();
}
接口实现:类使用implements关键字实现接口。在类声明中,implements关键字放在class声明后面。接口支持多重继承,即一个类可以同时实现多个接口。
class Cat implements Animal {
public void eat() {
System.out.println("eat");
}
public void sleep() {
System.out.println("sleep");
}
}
类需要对接口中的每一个方法都给出实现。我们可以使用接口类型来声明一个变量,那么这个变量可以引用到一个实现该接口的对象。比如:
Animal cat = new Cat()
通过接口来声明变量,可以让程序更具有扩展性,因为将来我们更方便替换接口的实现。
24.抽象类和接口的比较:
相同点:
都不能被实例化
都包含抽象方法,这些抽象方法用于描述系统能提供哪些服务,而这些服务是由子类来提供实现的
在系统设计上,两者都代表系统的抽象层,当一个系统使用一棵继承树上的类时,应该尽量把引用变量声明为继承树的上层抽象类型,这样可以提高两个系统之间的松耦合
不同点:
在抽象类中可以为部分方法提供默认的实现,从而避免在子类中重复实现它们;但是抽象类不支持多继承。接口不能提供任何方法的实现,但是支持多继承。
接口代表了接口定义者和接口实现者的一种契约;而抽象类和具体类一般而言是一种is-a的关系,即两者在概念本质上是不同的。
25.Java异常:异常定义了程序中遇到的非致命的错误,比如如程序要打开一个不存的文件、网络连接中断、除零操作、操作数越界、装载一个不存在的类等情况。我们将可能出现异常的代码通过try/catch代码进行了处理,当异常发生时,系统能够继续运行,而没有意外终止。当try代码块中的语句发生了异常,程序就会跳转到catch代码块中执行,执行完catch代码块中的程序代码后,系统会继续执行catch代码块之后的代码,try代码块中发生异常语句后的代码则不会再执行。
26.throws关键字:下面是一个代码实例。
package com.tianmaying;
public class HelloWorld {
private static void fun() {
int x = 5 / 0;
System.out.println(x);
}
public static void main(String[] args) {
try {
fun();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("program is still running here!");
}
}
面对的是一个fun()函数,如何知道这个函数是否需要调用添加try/catch处理呢? 在Java中,这个问题是交给被调用的方法的实现者来解决的。在这个例子中,定义fun()方法的时候,我们在方法参数列表后面增加一个throws关键字,然后增加这个方法可能抛出的异常,这种情况下调用者就必须使用try/catch进行处理了,否则编译将无法通过。因此将代码改为:
public class HelloWorld {
private static void foo() throws Exception {
int x = 5 / 0;
System.out.println(x);
}
public static void main(String[] args) {
try {
foo();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("program is still running here!");
}
}
如果一个方法中的语句执行时可能生成某种异常,但是并不能确定如何处理,则此方法应声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。也就是如果程序中的异常没有用try/catch捕捉异常以及处理异常的代码,我们可以在程序代码所在的函数(方法)声明后用throws声明该函数要抛出异常,将该异常抛出到该函数的调用函数中。
Exception类是java.lang.Throwable类的子类。在实际应用中,我们一般是使用Exception的子类来描述特定的异常的。Exception类是所有异常类的父类,Java语言为我们提供了许多Exception类的子类,分别对应不同的异常类型,例如:
ArithmeticException(在算术运算中发生的异常,如除以零)
NullPointerException(变量还没有指向一个对象,就引用这个对象的成员)
ArrayIndexOutOfBoundsException(访问数组对象中不存在的元素)
使用Java内置的异常类可以描述在编程时出现的大部分异常情况。除此之外,用户还可以自定义异常。用户自定义异常类,只需继承Exception类即可。在程序中使用自定义异常类,大体可分为以下几个步骤:(1)创建自定义异常类(2)在方法中通过throw关键字抛出异常对象(3)如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理;否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作(4)在出现异常方法的调用者中捕获并处理异常。
public class BlogAppException extends Exception {//继承Exception异常类
private static final long serialVersionUID = 1L;
private String command;// 可以给自定义异常增加成员变量,用以保存额外的异常信息
public BlogAppException(String command) {
this.command = command;
}
public String toString(){
return "Exception happened when executing command " + command;
}
}
我们来增加一个可以抛出这个异常的方法,并且在main方法中进行调用:
public class HelloWorld {
private static void bar() throws BlogAppException {
System.out.println("let's assume BlogAppException happened when executing `create` command");
// 为了演示,这里我们假设执行create命令时,抛出了异常
throw new BlogAppException("create");
}
private static void foo() throws ArithmeticException {
int x = 5 / 0;
System.out.println(x);
}
public static void main(String[] args) {
try {
foo();
bar();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("program is still running here!");
}
}
在catch多个异常时,如果使用catch(Exception e)语句,那么它不能放在其他catch语句的前面,否则后面的catch永远得不到执行,因为Exception是所有异常的父类。另外关于异常,在继承时需要注意两点:(1)一个方法被覆盖时,覆盖它的方法必须扔出相同的异常或异常的子类。(2)如果父类抛出多个异常,那么重写(覆盖)方法必须扔出那些异常的一个子集,也就是说,不能扔出新的异常。
在try/catch语句后,我们还可以有个finally语句,finally语句中的代码块不管异常是否被捕获总是要被执行的。finally还有一个特殊之处在于,即使try代码块和catch代码块中使用了return语句退出当前方法或break跳出某个循环 ,相关的finally代码块都要执行。finally中的代码块不能被执行的唯一情况是:在被保护代码块中执行了System.exit(0)。
27.Java IO:下面的代码通过FileInoutStream和FileOutputStream来完成文件内容的拷贝:
import java.io.*;
public class CopyFileByte {
public static void main(String args[]) throws IOException
{
FileInputStream in = null;
FileOutputStream out = null;
try {
in = new FileInputStream("input.txt");
out = new FileOutputStream("output.txt");
int c;
while ((c = in.read()) != -1) { //返回-1 表示达到文件结尾,首先判断是否到文件结尾,没有的话赋值给c
out.write(c);
}
}finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
}
InputStream和OutputStream是字节流处理,最小单位是一个字节8bits,而Java IO中还有一种字符流,它们处理的单位是16bits。
import java.io.*;
class ConsoleInOut {
public static void main(String args[])throws IOException{
InputStreamReader isr = null;
try {
isr = new InputStreamReader(System.in);//控制台输入,而不是标准输入类似于上面的"input.txt"
System.out.println("Enter characters, 'q' to quit.");
char c;
do {
c = (char) isr.read();//第一次键盘键入q的时候,还是会输出q
System.out.println(c);
} while(c != 'q');
}finally {
if (isr != null) {
isr.close();
}
}
}
}
在这两个语句当中,我们可以看到对异常的处理都是用try...finally,而不是try...catch...finally的形式,那什么时候使用try-finally,什么时候使用try-catch-finally呢?很显然这取决于方法本身是否能够处理try中出现的异常。如果自己可以处理,那么直接catch住,不用抛给方法的调用者;如果自己不知道怎么处理,就应该将异常向外抛,能够让调用者知道发生了异常。即在方法的签名中声明throws可能出现而自己又无法处理的异常,但是在方法内部做自己应该的事情。
28.数组和集合的区别:(1)数组声明容纳元素的类型,集合不声明 (2)数组是静态的,大小固定,集合不固定 (3)数组存放的类型只能是一种,集合可以有多种,不加泛型时类型为Object (4)数组由于是java内置的数据类型,执行效率快 (5)ArrayList就是基于数组创建的容器类
29.List是一个接口,而ArrayList是List接口的一个实现类。 ArrayList类继承并实现了List接口。因此,List接口不能被构造,也就是我们说的不能创建实例对象,但是我们可以像下面那样为List接口创建一个指向自己的对象引用,而ArrayList实现类的实例对象就在这充当了这个指向List接口的对象引用。
30.多态:理解多态前先了解一下重载和覆盖这两个概念。
一个方法名,参数不同,这叫方法重载。(Overload)
void foo(String str);
void foo(int number);
父类与子类有同样的方法名和参数,这叫方法覆盖。(Override)
class Parent {
void foo() {
System.out.println("Parent foo()");
}
}
class Child extends Parent {
void foo() {
System.out.println("Child foo()");
}
}
父类引用指向子类对象,调用方法时会调用子类的实现,而不是父类的实现,这叫多态。
Parent instance = new Child();
instance.foo(); //==> Child foo()
31.Object类:Object类在Java中被定义为一个顶级父类,它是任何类的父类,我们可以显示的继承它,也可以隐式继承,如以下实例
public class Dog extends Object{
}
与
public class Dog{
}
二者完全相同,Object类包含如下方法,1.equals(Object obj) 2.finalize() 3.getClass 4. hashCode() 5. notify 6. notifyAll() 7. wait() 8. Clone() 9. toString()由于Object类是任何类的父类,也就是说对Java中任意一个对象,都是可以用这九种方法的。
32.IO流:主要搞清楚InputStream,Reader,InputStreamReader,FileInputStream,BufferedReader,FileReader,BufferedInputStream.DataInputStream.
所有输入流都是InputStream(字节输入流)和Reader(字符输入流)的子类,InputStreamReader是字节流和字符流之间的桥梁,能将字节流输出为字符流。熟悉了这两点其他的就很好说了,其他的输入流都是作为它们的子类。
InputStream:字节输入流,在搞清楚字节输入流之前,我们需要搞清楚字节,所谓字节(Byte),指的是一种计量单位,表示数据量多少。例如“11数3A+、”所占的字节数为9,怎么来的呢?阿拉伯数字和英文字符占的字节数为1,中文字符“数”和中文符号“、”占的字节数为2,所以字节数为9。
Reader:字符是指计算机中使用的文字和符号,比如“1、2、3、A、B、C、~!·#¥%……—*()——+、”等等。