上一篇跟各位童鞋介绍了SharedPreference 和 File流如何存储数据,并且推荐使用FileOutputStream/FileInputStream来存储咱们游戏数据,那么这一篇则是像大家介绍另外一种适合游戏数据存储的方式:SQLite 轻量级数据库!

先介绍几个基本概念知识:

什么是SQLite:

SQLite是一款轻量级数据库,它的设计目的是嵌入式,而且它占用的资源非常少,在嵌入式设备中,只需要几百KB!!!!!

SQLite的特性:

  • 轻量级
    使用 SQLite 只需要带一个动态库,就可以享受它的全部功能,而且那个动态库的尺寸想当小。
  • 独立性
    SQLite 数据库的核心引擎不需要依赖第三方软件,也不需要所谓的“安装”。
  • 隔离性
    SQLite 数据库中所有的信息(比如表、视图、触发器等)都包含在一个文件夹内,方便管理和维护。
  • 跨平台
    SQLite 目前支持大部分操作系统,不至电脑操作系统更在众多的手机系统也是能够运行,比如:Android。
  • 多语言接口
    SQLite 数据库支持多语言编程接口。
  • 安全性
    SQLite 数据库通过数据库级上的独占性和共享锁来实现独立事务处理。这意味着多个进程可以在同一时间从同一数据库读取数据,但只能有一个可以写入数据.

优点:1.能存储较多的数据。

2.能将数据库文件存放到SD卡中!

什么是 SQLiteDatabase?

一个 SQLiteDatabase 的实例代表了一个SQLite 的数据库,通过SQLiteDatabase 实例的一些方法,我们可以执行SQL 语句,对数 据库进行增、删、查、改的操作。需要注意的是,数据库对于一个应用来说是私有的,并且在一个应用当中,数据库的名字也是惟一的。

什么是 SQLiteOpenHelper ?

根据这名字,我们可以看出这个类是一个辅助类。这个类主要生成一个数据库,并对数据库的版本进行管理。当在程序当中调用这个类的 方法getWritableDatabase(),或者getReadableDatabase()方法的时候,如果当时没有数据,那么Android 系统就会自动生成一 个数 据库。SQLiteOpenHelper 是一个抽象类,我们通常需要继承它,并且实现里边的3 个函数,

什么是 ContentValues 类?

ContentValues 类和Hashmap/Hashtable 比较类似,它也是负责存储一些名值对,但是它存储的名值对当中的名是一个String 类型,而值都是基本类型。

什么是 Cursor ?

Cursor 在Android 当中是一个非常有用的接口,通过Cursor 我们可以对从数据库查询出来的结果集进行随 机的读写访问。

OK,基本知识就介绍到这里,下面开始上代码:还是按照我的一贯风格,代码中该解释的地方都已经在代码中及时注释和讲解了!

顺便来张项目截图:

先给出xml:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:orientation="vertical" android:layout_width="fill_parent"
  4. android:layout_height="fill_parent">
  5. <TextView android:layout_width="fill_parent"
  6. android:layout_height="wrap_content" android:text="SQL 练习!(如果你使用的SD卡存储数据方式,为了保证正常操作,请你先点击创建一张表然后再操作)"
  7. android:textSize="20sp" android:textColor="#ff0000" android:id="@+id/tv_title" />
  8. <Button android:id="@+id/sql_addOne" android:layout_width="fill_parent"
  9. android:layout_height="wrap_content" android:text="插入一条记录"></Button>
  10. <Button android:id="@+id/sql_check" android:layout_width="fill_parent"
  11. android:layout_height="wrap_content" android:text="查询数据库"></Button>
  12. <Button android:id="@+id/sql_edit" android:layout_width="fill_parent"
  13. android:layout_height="wrap_content" android:text="修改一条记录"></Button>
  14. <Button android:id="@+id/sql_deleteOne" android:layout_width="fill_parent"
  15. android:layout_height="wrap_content" android:text="删除一条记录"></Button>
  16. <Button android:id="@+id/sql_deleteTable" android:layout_width="fill_parent"
  17. android:layout_height="wrap_content" android:text="删除数据表单"></Button>
  18. <Button android:id="@+id/sql_newTable" android:layout_width="fill_parent"
  19. android:layout_height="wrap_content" android:text="新建数据表单"></Button>
  20. </LinearLayout>

xml中定义了我们需要练习用到的几个操作按钮,这里不多解释了,下面看java源码:先看我们继承的 SQLiteOpenHelper 类

  1. package com.himi;
  2. import android.content.Context;
  3. import android.database.sqlite.SQLiteDatabase;
  4. import android.database.sqlite.SQLiteOpenHelper;
  5. import android.util.Log;
  6. /**
  7. *
  8. * @author Himi
  9. * @解释 此类我们只需要传建一个构造函数 以及重写两个方法就OK啦、
  10. *
  11. */
  12. public class MySQLiteOpenHelper extends SQLiteOpenHelper {
  13. public final static int VERSION = 1;// 版本号
  14. public final static String TABLE_NAME = "himi";// 表名
  15. public final static String ID = "id";// 后面ContentProvider使用
  16. public final static String TEXT = "text";
  17. public static final String DATABASE_NAME = "Himi.db";
  18. public MySQLiteOpenHelper(Context context) {
  19. // 在Android 中创建和打开一个数据库都可以使用openOrCreateDatabase 方法来实现,
  20. // 因为它会自动去检测是否存在这个数据库,如果存在则打开,不过不存在则创建一个数据库;
  21. // 创建成功则返回一个 SQLiteDatabase对象,否则抛出异常FileNotFoundException。
  22. // 下面是来创建一个名为"DATABASE_NAME"的数据库,并返回一个SQLiteDatabase对象
  23. super(context, DATABASE_NAME, null, VERSION);
  24. }
  25. @Override
  26. // 在数据库第一次生成的时候会调用这个方法,一般我们在这个方法里边生成数据库表;
  27. public void onCreate(SQLiteDatabase db) {
  28. String str_sql = "CREATE TABLE " + TABLE_NAME + "(" + ID
  29. + " INTEGER PRIMARY KEY AUTOINCREMENT," + TEXT + " text );";
  30. // CREATE TABLE 创建一张表 然后后面是我们的表名
  31. // 然后表的列,第一个是id 方便操作数据,int类型
  32. // PRIMARY KEY 是指主键 这是一个int型,用于唯一的标识一行;
  33. // AUTOINCREMENT 表示数据库会为每条记录的key加一,确保记录的唯一性;
  34. // 最后我加入一列文本 String类型
  35. // ----------注意:这里str_sql是sql语句,类似dos命令,要注意空格!
  36. db.execSQL(str_sql);
  37. // execSQL()方法是执行一句sql语句
  38. // 虽然此句我们生成了一张数据库表和包含该表的sql.himi文件,
  39. // 但是要注意 不是方法是创建,是传入的一句str_sql这句sql语句表示创建!!
  40. }
  41. @Override
  42. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  43. // 一般默认情况下,当我们插入 数据库就立即更新
  44. // 当数据库需要升级的时候,Android 系统会主动的调用这个方法。
  45. // 一般我们在这个方法里边删除数据表,并建立新的数据表,
  46. // 当然是否还需要做其他的操作,完全取决于游戏需求。
  47. Log.v("Himi", "onUpgrade");
  48. }
  49. }

我喜欢代码中立即附上解释,感觉这样代码比较让大家更容易理解和寻找,当然如果童鞋们不喜欢,可以告诉我,我改~嘿嘿~

下面看最重要的MainActivity中的代码:

  1. package com.himi;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import android.app.Activity;
  5. import android.content.ContentValues;
  6. import android.database.Cursor;
  7. import android.database.sqlite.SQLiteDatabase;
  8. import android.os.Bundle;
  9. import android.view.View;
  10. import android.view.Window;
  11. import android.view.WindowManager;
  12. import android.view.View.OnClickListener;
  13. import android.widget.Button;
  14. import android.widget.TextView;
  15. // ------------第三种保存方式--------《SQLite》---------
  16. /**
  17. * @author Himi
  18. * @保存方式:SQLite 轻量级数据库、
  19. * @优点: 可以将自己的数据存储到文件系统或者数据库当中, 也可以将自己的数据存
  20. *         储到SQLite数据库当中,还可以存到SD卡中
  21. * @注意1:数据库对于一个游戏(一个应用)来说是私有的,并且在一个游戏当中,
  22. *         数据库的名字也是唯一的。
  23. * @注意2 apk中创建的数据库外部的进程是没有权限去读/写的,
  24. *         我们需要把数据库文件创建到sdcard上可以解决类似问题.
  25. * @注意3 当你删除id靠前的数据或者全部删除数据的时候,SQLite不会自动排序,
  26. *        也就是说再添加数据的时候你不指定id那么SQLite默认还是在原有id最后添加一条新数据
  27. * @注意4 android 中 的SQLite 语法大小写不敏感,也就是说不区分大小写;
  28. *
  29. */
  30. public class MainActivity extends Activity implements OnClickListener {
  31. private Button btn_addOne, btn_deleteone, btn_check, btn_deleteTable,
  32. btn_edit, btn_newTable;
  33. private TextView tv;
  34. private MySQLiteOpenHelper myOpenHelper;// 创建一个继承SQLiteOpenHelper类实例
  35. private SQLiteDatabase mysql ;
  36. //---------------以下两个成员变量是针对在SD卡中存储数据库文件使用
  37. //  private File path = new File("/sdcard/himi");// 创建目录
  38. //  private File f = new File("/sdcard/himi/himi.db");// 创建文件
  39. @Override
  40. public void onCreate(Bundle savedInstanceState) {
  41. super.onCreate(savedInstanceState);
  42. getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
  43. WindowManager.LayoutParams.FLAG_FULLSCREEN);
  44. this.requestWindowFeature(Window.FEATURE_NO_TITLE);
  45. setContentView(R.layout.main);
  46. tv = (TextView) findViewById(R.id.tv_title);
  47. btn_addOne = (Button) findViewById(R.id.sql_addOne);
  48. btn_check = (Button) findViewById(R.id.sql_check);
  49. btn_deleteone = (Button) findViewById(R.id.sql_deleteOne);
  50. btn_deleteTable = (Button) findViewById(R.id.sql_deleteTable);
  51. btn_newTable = (Button) findViewById(R.id.sql_newTable);
  52. btn_edit = (Button) findViewById(R.id.sql_edit);
  53. btn_edit.setOnClickListener(this);
  54. btn_addOne.setOnClickListener(this);
  55. btn_check.setOnClickListener(this);
  56. btn_deleteone.setOnClickListener(this);
  57. btn_deleteTable.setOnClickListener(this);
  58. btn_newTable.setOnClickListener(this);
  59. myOpenHelper = new MySQLiteOpenHelper(this);// 实例一个数据库辅助器
  60. //备注1  ----如果你使用的是将数据库的文件创建在SD卡中,那么创建数据库mysql如下操作:
  61. //      if (!path.exists()) {// 目录存在返回false
  62. //          path.mkdirs();// 创建一个目录
  63. //      }
  64. //      if (!f.exists()) {// 文件存在返回false
  65. //          try {
  66. //              f.createNewFile();//创建文件
  67. //          } catch (IOException e) {
  68. //              // TODO Auto-generated catch block
  69. //              e.printStackTrace();
  70. //          }
  71. //      }
  72. }
  73. @Override
  74. public void onClick(View v) {
  75. try {
  76. //备注2----如果你使用的是将数据库的文件创建在SD卡中,那么创建数据库mysql如下操作:
  77. //              mysql = SQLiteDatabase.openOrCreateDatabase(f, null);
  78. //备注3--- 如果想把数据库文件默认放在系统中,那么创建数据库mysql如下操作:
  79. mysql = myOpenHelper.getWritableDatabase(); // 实例数据库
  80. if (v == btn_addOne) {// 添加数据
  81. // ---------------------- 读写句柄来插入---------
  82. // ContentValues 其实就是一个哈希表HashMap, key值是字段名称,
  83. //Value值是字段的值。然后 通过 ContentValues 的 put 方法就可以
  84. //把数据放到ContentValues中,然后插入到表中去!
  85. ContentValues cv = new ContentValues();
  86. cv.put(MySQLiteOpenHelper.TEXT, "测试新的数据");
  87. mysql.insert(MySQLiteOpenHelper.TABLE_NAME, null, cv);
  88. // inser() 第一个参数 标识需要插入操作的表名
  89. // 第二个参数 :默认传null即可
  90. // 第三个是插入的数据
  91. // ---------------------- SQL语句插入--------------
  92. // String INSERT_DATA =
  93. // "INSERT INTO himi (id,text) values (1, '通过SQL语句插入')";
  94. // db.execSQL(INSERT_DATA);
  95. tv.setText("添加数据成功!点击查看数据库查询");
  96. } else if (v == btn_deleteone) {// 删除数据
  97. // ---------------------- 读写句柄来删除
  98. mysql.delete("himi", MySQLiteOpenHelper.ID + "=1", null);
  99. // 第一个参数 需要操作的表名
  100. // 第二个参数为 id+操作的下标 如果这里我们传入null,表示全部删除
  101. // 第三个参数默认传null即可
  102. // ----------------------- SQL语句来删除
  103. // String DELETE_DATA = "DELETE FROM himi WHERE id=1";
  104. // db.execSQL(DELETE_DATA);
  105. tv.setText("删除数据成功!点击查看数据库查询");
  106. } else if (v == btn_check) {// 遍历数据
  107. //备注4------
  108. Cursor cur = mysql.rawQuery("SELECT * FROM "
  109. + MySQLiteOpenHelper.TABLE_NAME, null);
  110. if (cur != null) {
  111. String temp = "";
  112. int i = 0;
  113. while (cur.moveToNext()) {//直到返回false说明表中到了数据末尾
  114. temp += cur.getString(0);
  115. // 参数0 指的是列的下标,这里的0指的是id列
  116. temp += cur.getString(1);
  117. // 这里的0相对于当前应该是咱们的text列了
  118. i++;
  119. temp += "  "; // 这里是我整理显示格式 ,呵呵~
  120. if (i % 3 == 0) // 这里是我整理显示格式 ,呵呵~
  121. temp += "/n";// 这里是我整理显示格式 ,呵呵~
  122. }
  123. tv.setText(temp);
  124. }
  125. } else if (v == btn_edit) {// 修改数据
  126. // ------------------------句柄方式来修改 -------------
  127. ContentValues cv = new ContentValues();
  128. cv.put(MySQLiteOpenHelper.TEXT, "修改后的数据");
  129. mysql.update("himi", cv, "id " + "=" + Integer.toString(3), null);
  130. // ------------------------SQL语句来修改 -------------
  131. // String UPDATA_DATA =
  132. // "UPDATE himi SET text='通过SQL语句来修改数据'  WHERE id=1";
  133. // db.execSQL(UPDATA_DATA);
  134. tv.setText("修改数据成功!点击查看数据库查询");
  135. } else if (v == btn_deleteTable) {// 删除表
  136. mysql.execSQL("DROP TABLE himi");
  137. tv.setText("删除表成功!点击查看数据库查询");
  138. } else if (v == btn_newTable) {// 新建表
  139. String TABLE_NAME = "himi";
  140. String ID = "id";
  141. String TEXT = "text";
  142. String str_sql2 = "CREATE TABLE " + TABLE_NAME + "(" + ID
  143. + " INTEGER PRIMARY KEY AUTOINCREMENT," + TEXT
  144. + " text );";
  145. mysql.execSQL(str_sql2);
  146. tv.setText("新建表成功!点击查看数据库查询");
  147. }
  148. // 删除数据库:
  149. // this.deleteDatabase("himi.db");
  150. } catch (Exception e) {
  151. tv.setText("操作失败!");
  152. } finally {// 如果try中异常,也要对数据库进行关闭
  153. mysql.close();
  154. }
  155. }
  156. }

以上代码中我们实现了两种存储方式:

一种存储默认系统路径/data-data-com.himi-databases下,另外一种则是保存在了/sdcard-himi下,生成数据库文件himi.db

那么这里两种实现方式大概步骤和区别说下:

-----------如果我们使用默认系统路径存储数据库文件:

第一步:新建一个类继承SQLiteOpenHelper;写一个构造,重写两个函数!

第二步:在新建的类中的onCreate(SQLiteDatabase db) 方法中创建一个表;

第三步:在进行删除数据、添加数据等操作的之前我们要得到数据库读写句柄得到一个数据库实例;

    注意: 继承写这个辅助类,是为了在我们没有数据库的时候自动为我们生成一个数据库,并且生成数据库文件,这里也同时创建了一张表,因为我们在onCreate里是在数据库中创建一张表的操作;这里还要注意在我们new 这个我们这个MySQLiteOpenHelper 类实例对象的时候并没有创建数据库哟~!而是在我们调用 (备注3)MySQLiteOpenHelper ..getWritableDatabase() 这个方法得到数据库读写句柄的时候,android 会分析是否已经有了数据库,如果没有会默认为我们创建一个数据库并且在系统路径data-data-com.himi-databases下生成himi.db 文件!(如果我们使用sd卡存储数据库文件,就没有必要写这个类了,而是我们自己Open自己的文件得到一个数据库,西西,反而方便~ )

-----------如果我们需要把数据库文件存储到SD卡中:

第一步:确认模拟器存在SD卡,关于SD卡的两种创建方法见我的博文:【Android 2D游戏开发之十】

第二步:(备注1)先创建SD卡目录和路径已经我们的数据库文件!这里不像上面默认路径中的那样,如果没有数据库会默认系统路径生成一个数据库和一个数据库文件!我们必须手动创建数据库文件!

第三步:在进行删除数据、添加数据等操作的之前我们要得到数据库读写句柄得到一个数据库实例;(备注2)此时的创建也不是像系统默认创建,而是我们通过打开第一步创建好的文件得到数据库实例。这里仅仅是创建一个数据库!!!!

第四步:在进行删除数据、添加数据等操作的之前我们还要创建一个表!

第五步:在配置文件AndroidMainfest.xml 声明写入SD卡的权限,上一篇已经介绍权限了,不知道的自己去看下吧。

    有些童鞋不理解什么默认路径方式中就有表?那是因为我们在它默认给我们创建数据库的时候我们有创建表的操作,就是MySQLiteOpenHelper类中的onCreate()方法里的操作!所以我们如果要在进行删除数据、添加数据等操作的之前还要创建一个表,创建表的方法都是一样的。

    总结:不管哪种方式我们都要-创建数据库-创建表-然后进行操作!

备注4:

在Android中查询数据是通过Cursor类来实现的,当我们使用SQLiteDatabase.query()方法时,会得到一个Cursor对象,Cursor指向的就是每一条数据。它提供了很多有关查询的方法,具体方法如下:

以下是方法和说明:

move 以当前的位置为参考,将Cursor移动到指定的位置,成功返回true, 失败返回false

moveToPosition 将Cursor移动到指定的位置,成功返回true,失败返回false

moveToNext 将Cursor向前移动一个位置,成功返回true,失败返回false

moveToLast 将Cursor向后移动一个位置,成功返回true,失败返回 false。

movetoFirst 将Cursor移动到第一行,成功返回true,失败返回false

isBeforeFirst 返回Cursor是否指向第一项数据之前

isAfterLast 返回Cursor是否指向最后一项数据之后

isClosed 返回Cursor是否关闭

isFirst 返回Cursor是否指向第一项数据

isLast 返回Cursor是否指向最后一项数据

isNull 返回指定位置的值是否为null

getCount 返回总的数据项数

getInt 返回当前行中指定的索引数据

对于SQLite的很多童鞋有接触过,但是就不知道怎么存储在SD中,所以我也研究了下,这篇也写了把sd卡中的方式也提供给大家。

OK 这元旦放假几天就光给大家写这个了,呵呵~凌晨2点了,咳咳~该睡觉了。(我一般凌成3点睡觉,早8点起来上班 呵呵~习惯了~)

本文转自 xiaominghimi 51CTO博客,原文链接:http://blog.51cto.com/xiaominghimi/606759,如需转载请自行联系原作者

【Android游戏开发十三】(保存游戏数据 [下文])详解SQLite存储方式相关推荐

  1. Android 应用开发(41)---EditText(输入框)详解

    EditText(输入框)详解 1.设置默认提示文本 如下图,相信你对于这种用户登录的界面并不陌生,是吧,我们很多时候都用的这种界面 相比另外这种,下面这种又如何? 还不赖是吧,当然,不会在这里贴布局 ...

  2. android fragment传递数据,Android 两个Fragment之间传递数据实例详解

    Android 两个Fragment之间如何传递数据 FragmentA启动FragmentB,做一些选择操作后,返回FragmentA,需要把FragmentB里面选择的数据传回来.有什么办法? F ...

  3. Android模仿iPhone View旋转刷新数据动画详解

    因为小马很喜欢在不同的页面之间跳转时加点好玩的动画,今天无意间看到一个动画效果感觉不错,几种效果图如下,既然好玩就写在博客中,直接说就是:该效果类似于iPhone中View的切换动画效果,今天就只介绍 ...

  4. 使用Android studio开发一个数独游戏APP 系列第一讲

    数独是一种需要进行演算的逻辑游戏.玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并且满足每一行.每一列.每一个粗线宫内的数字均含1-9,不能重复.随着各种报刊杂志刊登了数独游戏,也让越 ...

  5. libGDX游戏开发之修改游戏帧数FPS(十三)

    libGDX游戏开发之修改游戏帧数FPS(十三) libGDX系列,游戏开发有unity3D巴拉巴拉的,为啥还用java开发?因为我是Java程序员emm-国内用libgdx比较少,多数情况需要去官网 ...

  6. 【邀您参加】Android大讲堂-Android游戏开发之捕鱼游戏

    来源:华清远见嵌入式培训中心 ● 揭开Android底层架构的神秘面纱 Android应用开发与底层开发一网打尽! ● 全程免费 网络直播 实时互动 业内专家精辟讲解 项目案例透彻分析 作为目前全球最 ...

  7. 基于cocos2d-x的快速的游戏开发--回合制游戏

    2019独角兽企业重金招聘Python工程师标准>>> #基于cocos2d-x的快速的游戏开发--回合制游戏 开发时间:3天 开发工具:cocos2d-x和cocostudio 开 ...

  8. flex 联机游戏开发 - 四国军棋游戏:(三) 核心类

    通过前面的练习 flex 联机游戏开发 - 四国军棋游戏:(二)棋盘棋子 我们已经成功地用不到200行的代码绘制出了一个漂亮的棋盘,现在,我们开始进入游戏的核心类设计阶段.四国军棋的游戏逻辑还是比较复 ...

  9. flex 联机游戏开发 - 四国军棋游戏:(三) 核心类设计

    通过前面的练习 flex 联机游戏开发 - 四国军棋游戏:(二)棋盘棋子 我们已经成功地用不到200行的代码绘制出了一个漂亮的棋盘,现在,我们开始进入游戏的核心类设计阶段.四国军棋的游戏逻辑还是比较复 ...

最新文章

  1. java 取整型的低八位_Java 基础(十五)并发工具包 concurrent
  2. JSONP的实现原理
  3. MyEclipse或者Eclipse内存溢出问题
  4. 从近年CVPR看域自适应立体匹配
  5. css 旋转 animation动画
  6. Linux:详细的RPM和YUM/DNF管理工具,配置仓库等
  7. Spark SQL操作多数据源
  8. 什么是长连接和短连接,他们的定义区别及应用场景?
  9. win10的怎么调计算机亮度,win10电脑亮度怎么调
  10. 真实吐槽点评:华为nova8SE和华为nova7Pro区别-哪个更值得入手-参数对比
  11. Java 版本任你发,我用Java8.(Java 15 新功能介绍 )
  12. 循环语句(while循环)
  13. pytorch runtime error(59):device-side assert triggered at XXX
  14. java web atm机_java笔记——模拟ATM机例子
  15. 什么是生命周期?生命周期函数有哪些?每个函数的功能是什么?
  16. BIG5到GB的转换技术
  17. 计算机丢失ch365dll,修复ch365dll.dll
  18. Linux:详解talk服务的启用和talk命令使用
  19. 从PaaS平台到技术中台,BoCloud博云直指“数字中国的架构师”
  20. Visual Studio2017专业版和企业版密钥

热门文章

  1. elipse+pydev+python开发arcgis脚本程序
  2. JSON 之 SuperObject(17): 实例 - 借用 Google 实现全文翻译
  3. 衡量模块独立性的两个定性标准
  4. Trident API 概览
  5. 在storm中使用定时保存
  6. 解决LSP问题导致无法上网
  7. 关于磁盘爆满异常(Linux下已经删除文件继续占用空间问题)
  8. 服务器主板稳定,服务器主板
  9. os的java版本_如何在OS X上设置或更改默认的Java(JDK)版本?
  10. ubuntu 破解mysql密码_Ubuntu下忘记MySQL root密码解决方法