Android開發基礎 -- 實體類 和 抽象類 詳解
在日常的Java專案開發中,entity(實體類)是必不可少的,它們一般都有很多的屬性,並有相應的setter和getter方法。entity(實體類)的作用一般是和資料表做對映。所以快速寫出規範的entity(實體類)是java開發中一項必不可少的技能。
大概的說,實體類就是:屬性+get/set方法。
在專案中寫實體類一般遵循下面的規範:
1、
2、根據你的設計,定義一組你需要的私有屬性。(如:private int age;)
3、根據這些屬性,建立它們的setter和getter方法。(AndroidStudio 等整合開發軟體可以自動生成。具體怎麼生成請自行百度。)
4、提供有引數的構造器(所有的引數)和無引數的構造器。(如果你不手動寫上構造方法。會預設幫你加上一個無參構造方法[不會顯示出來])
關於構造器可以看看這幾篇文章(在下面我也會大概說下):
5、重寫父類中的eauals()方法和hashcode()方法。(如果需要涉及到兩個物件之間的比較,這兩個功能很重要。)
關於重寫這兩個方法的原因可以看看這幾篇文章:
如何重寫hashCode()和equals()方法1 , 如何重寫equals和 hashCode方法2
6、實體類應該實現Serializable介面(序列化)。(如:public class BaseEntity implements Serializable { } )
實體類為什麼要序列化請參考這篇文章和這個貼: java實體類實現序列化的意義 (詳細的解析) 和 Java序列化的意義(通俗易懂)
關於serialVersionUID作用介紹請看兩篇文章: serialVersionUID作用 和 AndroidStudio自動生成SerialVersionUID方法
下面是我寫的一個實體類(entity)例子:具體的細節都用註釋標註了。
/**一個學生的Java實體類*/
class Student implements Serializable{
/**
* 序列化版本號(自動生成,方法請看上面)
*/
private static final long serialVersionUID = 88722423642445L;
//定義的私有屬性
private int id;
private String name;
private int age;
private double score;
//無引數的構造器(如果不寫系統會自動生成無引數的構造器,但不會顯示出來)
public Student(){
}
//有引數的構造器
public Student(int id,String name,int age, double score){
this.id = id;
this.name = name;
this.age = age;
this.score = score;
}
//建立的setter和getter方法
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
//由於id對於學生這個類是唯一可以標識的,所以重寫了父類中的id的hashCode()和equals()方法。
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (id != other.id)
return false;
return true;
}
}
ps:
A. 關於構造器作用: 利用構造器引數初始化物件的屬性。
舉個例子:
public class Animal {
private int legs;
//無參構造器
public Animal() {
legs = 4;
}
//有參構造器
public Animal(int legs) {
this.legs = legs;
}
public void setLegs(int i) { legs = i; }
public int getLegs(){return legs;}
}
建立Animal類的例項:Animal a=new Animal(); //呼叫無參構造器,將legs初始化為4;(即 a.legs=4)
Animal a=new Animal(10); //呼叫有參構造器,將legs初始化為10;(即 a.legs=10)
註釋:構造器的名稱必須與類名相同。修飾符:public、private、protected
構造器不是方法,沒有返回值(連void也不能寫)
B. 關於重寫equals() 和 hashCode()方法的大致原因(具體介紹可以看看上面給出的那幾個文章地址) :
默 認equals在比較兩個物件時,是看他們是否指向同一個地址的。
但有時,我們希望兩個物件只要是某些屬性相同就認為他們的quals為true。比如:
Student s1 = new Student(1,"name1");
Student s2 = new Student(1,"name1");
如果不重寫equals的話,他們是不相同的,所以我們要重些equals,判斷只要他們的id和名字相同equals就為true,在一些集合裡有時也這樣用,集合裡的contain也是用equals來比較;
另外:equals()相等的兩個物件,hashcode()一定相等;
equals()不相等的兩個物件,卻並不能證明他們的hashcode()不相等。
實體類 總結: 實體是就是Java中的O/R Mapping對映,即資料庫中的一個表對映成對應的一個Java類,其中還有一個對映檔案。給定一個較複雜的實體關係(如一對一,一對多,多對多),應該熟練地寫出實體類!!
2. 抽象類
簡單的說,抽象類就是從一般類中抽取他們的共性封裝成的類,所以抽象類的特點就像抽象的事物一樣不是現實的不能被例項化。
比如動物是抽象類(父類);狗,貓等就是動物的具體類(例項化),即子類。
【如,貓狗的共性:四條腿,兩隻耳朵 等】
下面是關於抽象類的特點介紹:
1、 用abstract關鍵字來修飾一個類時,這個類叫做 抽象類;用abstract來修飾一個方法時,該方法叫做 抽象方法。(抽象方法一定定義在抽象類中)
2、 抽象方法沒有方法主體,就是沒有大括號,直接在小括號後面加分號。(如:public abstract void sayHello(); 就是抽象方法。)
3、抽象類中的方法要被使用,必須有子類覆寫所有的抽象方法後,建立子類物件呼叫。
4、含有抽象方法的類必須被宣告抽象類,抽象類必須被繼承,抽象方法必須被重寫。
5、抽象類不能被例項化 (即不能用new建立物件),因為沒有意義。
6、抽象方法只需宣告,而不需實現。
7、 抽象類就是為了讓子類繼承的,它表示從一些具體類中抽象出來的型別。如果想要繼承抽象類的話,必須要在子類中覆寫抽象類中的全部抽象方法才行。
8、抽象類裡面的方法只需宣告,無需寫方法體,方法體在子類中覆寫時寫入方法體就行了。
9、如果子類只覆蓋了部分抽象方法,那麼該子類還是一個抽象類;抽象類中即可有抽象方法也可以有非抽象方法。
10、抽象類中可以不定義抽象方法,這種作用僅僅用於不讓該類建立物件。
下面是一個簡單的抽象類例子:
/**
* 宣告抽象類;
*
* (父類)
**/
abstract class Animal{
private String name;
public Animal(String name){
this.name = name;
}
public abstract void enjoy(); //宣告抽象方法
}
/**
* 繼承抽象類;
*
**/
class Dog extends Animal{
private String forlorColor;
public Dog(String name,String forlorColor){
super(name);
this.forlorColor = forlorColor;
}
// 實現抽象類裡面的抽象方法 (即加些功能)
protected void enjoy(){
System.out.println("dog叫聲~~~");
}
}
注意:抽象類的繼承者必須實現抽象方法,否則也必須被定義成抽象的,由下一個繼承者實現。抽象類不能例項化物件。
又如,在Android開發中,經常會碰到自定義的 BaseActivity 、BaseFragment 等,這些就是大多數是抽象類;
我們在做Android應用的時候最基本的類當然是Activity了,我們一般的情況下要建立一個基類Activity然後讓別的Activity來繼承於它,但是我們可以使用一些小技巧來簡便我們對這個BaseActivity的使用,如下:
/**
* 繼承activity的抽象類
*/
public abstract class BaseActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//子類繼承該抽象類BaseActivity後就會按著這些抽象方法一步一步執行
findViews(); //先 繫結控制元件
init(); //然後 初始化檢視
setListeners(); //最後 設定監聽器
}
//宣告抽象方法
protected abstract void findViews();
protected abstract void init();
protected abstract void setListeners();
}
那麼我們來看下實現該抽象類:
/**
* 繼承抽象類,實現裡面的抽象方法
*/
public class MainActivity extends BaseActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
// 實現下面 抽象類裡面的抽象方法 (即加些功能等)
@Override
protected void findViews() {
// TODO Auto-generated method stub
//先在這裡繫結控制元件
}
@Override
protected void init() {
// TODO Auto-generated method stub
//然後初始化檢視
}
@Override
protected void setListeners() {
// TODO Auto-generated method stub
//最後 設定監聽器
}
}
其實就是一個非常簡單的小知識點:
我們繼承於一個類,讓它成為抽象類,加上一些初始化的抽象方法,這樣我們再繼承於這個類的時候就必須去實現這些抽象方法,就不用我們去手動新增,這樣也不會讓我們丟失了一些操作,如初始化等,而且我們還把這些初始化的方法新增到了onCreate()方法中;
這樣我們的子類只要繼承於BaseActivity,有以下兩點好處:
(1)肯定我們不用再去託運新增實現findViews()init()setLinsteners()這些初始化方法了,只需在這些子類中實行這些抽象方法即可;
(2)我們不用再在每個Activity中手動呼叫這三個方法,因為我們的BaseActivity已經為我們做好了封裝;
OK,以後我們就這樣搭建我們的BaseActivity吧;
*******************************************************************
網上有很多介紹BaseActivity的博文,多數是從應用的角度去描述的。
這裡,我所介紹的BaseActivity不同,主要從框架搭建的角度去介紹BaseActivity的使用。
先看程式碼:
/**
* 應用程式Activity的基類
*/
public abstract class BaseActivity extends Activity implements
OnClickListener {
private static final int ACTIVITY_RESUME = 0;
private static final int ACTIVITY_STOP = 1;
private static final int ACTIVITY_PAUSE = 2;
private static final int ACTIVITY_DESTROY = 3;
public int activityState;
// 是否允許全屏
private boolean mAllowFullScreen = true;
//宣告抽象方法
public abstract void initWidget();
public abstract void widgetClick(View v);
public void setAllowFullScreen(boolean allowFullScreen) {
this.mAllowFullScreen = allowFullScreen;
}
@Override
public void onClick(View v) {
widgetClick(v);
}
/***************************************************************************
*
* 列印Activity生命週期
*
***************************************************************************/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AppLog.debug(this.getClass() + "---------onCreat ");
// 豎屏鎖定
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
if (mAllowFullScreen) {
requestWindowFeature(Window.FEATURE_NO_TITLE); // 取消標題
}
AppManager.getAppManager().addActivity(this);
initWidget();
}
@Override
protected void onStart() {
super.onStart();
AppLog.state(this.getClass(), "---------onStart ");
}
@Override
protected void onResume() {
super.onResume();
activityState = ACTIVITY_RESUME;
AppLog.state(this.getClass(), "---------onResume ");
}
@Override
protected void onStop() {
super.onResume();
activityState = ACTIVITY_STOP;
AppLog.state(this.getClass(), "---------onStop ");
}
@Override
protected void onPause() {
super.onPause();
activityState = ACTIVITY_PAUSE;
AppLog.state(this.getClass(), "---------onPause ");
}
@Override
protected void onRestart() {
super.onRestart();
AppLog.state(this.getClass(), "---------onRestart ");
}
@Override
protected void onDestroy() {
super.onDestroy();
activityState = ACTIVITY_DESTROY;
AppLog.state(this.getClass(), "---------onDestroy ");
AppManager.getAppManager().finishActivity(this);
}
}
定義一個初始化Activity控制元件的抽象方法initWidget();
像findviewbyid()這類程式碼就可以寫在這裡,不會影響程式碼結構了。這裡需要提一點的是:setContent()方法一定要寫在initWidget()裡,而不能再寫到oncreate裡面了,看程式碼可以知道,initwidget方法是存在於super()中的,而如果再寫到oncreate裡,就相當於先呼叫了findview再去呼叫setcontent,這樣肯定會報空指標異常。
關於豎屏鎖定,這個可以按需要新增,沒什麼說的。
還有一個要說的就是requestWindowFeature(Window.FEATURE_NO_TITLE); // 取消標題
對於這段程式碼,如果你要使用系統的ActionBar的時候,一點要記得呼叫setAllowFullScreen,設定為false,否則BaseActivity自動取消了ActionBar你又去使用,肯定也會出異常。
還有一點:Baseactivity已經實現了OnClickListener,所以子類無需再次實現,控制元件可以直接在initWidget裡面setonclicklistener(this);然後在widgetClick(View v)中設定監聽事件即可。
有關AppManager的內容我將放到下一篇《Android應用框架搭建》去講解,這裡大家可以先忽略。
有關生命週期的列印,我認為在除錯階段還是有必要的,畢竟看著每一個Activity的生命週期,如果出了問題馬上就可以清楚的知道是哪裡出了問題
其他關於BaseActivity的文章推薦:為你的Android應用定製屬於你的BaseActivity, Android App框架設計之編寫基類BaseActivity