博客统计信息

51cto推荐博客
用户名:kshark
文章数:24
评论数:86
访问量:115998
无忧币:282
博客积分:981
博客等级:3
注册日期:2008-03-16

我最近发表的评论

VC6.0不支持标准.. 回复
VC6.0 中应该使用__min() 和 __max(..
不值钱的系统分析师 回复
国家科举制度下,考出来的系统分析..
JDBC事务编程模型.. 回复
也不能说JDBC没有存在空间了,Sprin..
《Java组件设计》.. 回复
感谢小废物的答复,我现在一张一张..
《Java组件设计》.. 回复
图片非得一个一个传。如果直接粘贴..

背景音乐

我的音乐

00:00 | 00:00

    经过近半年的辛勤耕耘,《Java组件设计》一书终于完成,目前已经由电子工业出版社博文视点公司出版!
    本书面向软件架构师、设计师、高级开发人员,主要讲解企业应用系统中核心组件的设计原则与最佳实践,澄清设计模式、数据结构、多线程、接口设计等多个高阶技术领域中“流行”的认知误区,通过大量的组件实例分析,为读者精彩地讲解组件设计这一最具技术含量的领域需要考虑的核心问题、设计方案与最佳实践。
     本书提供具有工业级强度的企业组件代码实现,展现了Java..
4         配置组件
在本书的第二章:组件设计中,对组件设计有一个重要的原则,那就是组件自己不能带有任何配置文件。组件应该合理暴露出自己的接口方法,所有的配置行为,都应该通过接口方法的调用来进行,这样就可以满足各种不同环境集成的要求。
依据这个原则,每个应用都可以集成很多不同的组件,而这个应用本身只有一个配置文件,集中配置了这个应用所需要的所有配置信息。应用程序从这个配置文件中读取所有的配置信息,然后将配置参数通过组件的接口方法调用,设置到各个组件中。
..
3.5             线程
在高性能组件、框架和服务程序开发中,多线程并发处理的技术被广泛采用。合理的采用多线程,可以大大提高系统的处理能力和处理速度,提高硬件的利用率。多线程的控制能力,是组件开发者必备的基本技能。
3.5.1       线程基础
我们先来看看如何创建一个线程。创建一个线程有2种方法,一种是从Thread继承出一个子类,另一种是实现Runnable 接口。具体如下:
1)        ..
3.4             泛型
3.4.1       泛型简介
先拿一个例子来说明泛型是什么。
有两个类如下,要构造两个类的对象,并打印出各自的成员x。
public class StringFoo {
    private String x;
    public String getX() {
        return x;
    }
 
    public void setX(String x) {
     &nbs..
3.2             反射
我们通常写的程序,都是静态的代码,比如:调用class A示例的put 方法:
A a = new A();
a.put(“Hello!”);
第二行,a.put 就是一种静态的写法。在编译阶段,就完成对a类型的解析,对a是否具有put方法进行了判断。如果a对象没有put方法,则编译不通过。
可是,在另外一些情况下,我们需要在运行时动态的对一些对象的指定方法进行调用,比如我们想写一个通用的函数,传入一个对象,把这个对象的属性名和属性值都打印出来,这就需要使用反射技..
3         预备知识
本章主要讲解一些组件开发中常用的技术,作为后续章节讲解组件设计的预备知识。
本章假设读者已经熟悉了Java语言的基本语法,并且熟练使用Java语言至少3年,因此,简单的语法知识不再讲述,仅讲解一些高级主题。
3.1             Java语法深入
本节对Java语法中一些高级主题进行讲解。
3.1.1       static
3.1.1.1     static 变量
本质上讲,sta..
2.2             组件设计:无配置文件
组件自己不要带任何配置文件,组件所依赖的各种库也不带任何配置文件。
这个原则极为重要!!!
如果一个组件,自己定义了配置文件,无论是XML,Properties,还是其他的文件类型,内部的格式都是组件设计者自己定义的。我们假设一个中等的项目,会使用10个组件,那么就会有10个配置文件,这些配置文件的格式各不相同。那么我们的软件安装手册、维护手册就要仔细描述这10个配置文件的结构,我们的实施维护人员就要学会如何配置这..





2         组件设计原则
Java阵营一直倡导开源,开源运动如火如荼展开,催生了无数组件。但是,坦率的讲,这些开源的组件中,能够直接拿过来,不做任何改造,就能用于商业软件构建,满足功能和性能的要求,这样的优秀组件不多见。因此,核心软件开发者时常面对的尴尬局面是:大量的开源资源,都不满足我的要求。
实际上,组件设计是软件设计开发最精髓所在,凝聚了数据结构、面向对象、设计模式、线程并发同步、操作系统等诸多领域的最核心技术,一直是设计开发领域彰显技术水准..
编写计算机软件的人很多,我们通常都把这些活动称为软件开发。但是软件的种类是不同的,每种软件都有自身的复杂性和挑战性。本人一直工作在电信行业,电信行业的软件非常复杂,对并发、大数据量、性能、高可靠性要求很高,这些都对软件的设计和开发提出了严峻的挑战。
1.1             应用软件结构
通常,应用软件的总体结构,可以分为两大部分:应用层和平台层,具体如下:



图 1‑1 应用软件总体结构
平台层提供基础框架和大量可重用组件,这些组件以一定的接口方式暴露出来,供应用层来调用。平台层通常不提供与具体业务相关的逻辑处理,而是提供:
1)     业务无关的框架/功能组件:比如日志、安全、线程池、连接池、告警监控等
2)     多种业务之间可共用的机制:如工作流、事件通知机制等,这部分也与具体的业务无关。
应用层提供具体应用相关的逻辑处理部分,包括页面、应用逻辑、应用数据等。
平台层和应用层,是个逻辑划分的概念,实际软件实现中,平台层和应用层都可以由多个层来实现,也可以合并到一个程序中,这要视项目的规模和具体需求而定。
从上图可以看出,构建一个高度可重用的平台层,可以使应用开发只需集中精力关注业务逻辑,业务无关的功能组件和机制都由平台层提供,可以直接使用,这样极大简化了应用开发,缩短了软件交付周期,保障了软件质量。
而构建一个高度可重用的平台层,最核心的挑战就是设计和开发高度可重用的组件,提取应用的共性需求,简化接口,真正做到应用开发时可以直接拿来就用,而且非常好用。
1.2             组件定义
那么,到底什么是组件呢?框架又是什么意思?类是组件吗?控件又指什么?元件、构件这些概念又如何理解?
这些概念,都没有一个统一的标准答案,因此在软件开发过程中,这些术语经常被混淆,作者根据自己的工作体会,对这些概念解释如下:
1)     对象:面向对象语言(Object-Oriented Language)是一类以对象作为基本程序结构单位的程序设计语言,指用于描述的设计是以对象为核心,而对象是程序运行时刻的基本成分。对象都具有状态和行为。对象,也被翻译为实例、物件。
2)     类:面向对象的概念。类是一种对包括数据成员、函数成员和嵌套类型进行封装的数据结构。在程序运行期间,由类创建成对象的过程,叫类的实例化。因此,对象是个运行期的术语,类是个编译期的术语。类本身就是可重用的直接实现手段。
3)     组件:类本身是个细粒度的可重用实现,为了解决功能或机制层面更大粒度重用的问题,又引入了组件的概念。组件的英文是Component,其逻辑结构如下:



组件对外暴露一个或多个接口,供外界调用。组件内部由多个类来协同实现指定的功能。对于复杂的组件,会包括很多的类,还可能包含配置文件、界面、依赖的库文件等,组件也可以包含或者使用其它的组件,构成更大的组件。
一些特定范畴的组件,由软件厂家或者国际权威组织制定并颁布了组件规范,如COM、ActiveX、EJB、JavaBean等。本书讨论的组件,指一般意义的自定义组件,不包括这些规范化的组件。
4)     控件:控件的英文是Control,控件就是具有用户界面的组件。要说的具体一点,就得回顾早期 Windows 的历史根源,当时控件指任何子窗口:按钮、列表框、编辑框或者某个对话框中的静态文本。从概念上讲,这些窗口——控件——类似用来操作收音机或小电器的旋钮和按钮。随着控件数量的增加(组合框、日期时间控件等等),控件逐渐成为子窗口的代名词,无论是用在对话框中还是用在其它种类的主窗口中。没过多久 BASIC 程序员开始编写他们自己专用的控件,自然而然地人们便想到共享这些控件。共享代码的方法之一是通过磁盘拷贝,但那样显然效率低下。必须要有一种机制使开发者建立的控件能够在其它程序员的应用中轻而易举地插入,这便是VBA控件,OLE控件,OCX和最后ActiveX 控件产生的动机。因此,控件是组件的一个主要样本(并且历史上曾驱动着组件的开发),控件又不仅仅是唯一的一种组件。
5)     元件:元件是个电子行业的术语,是电子元件的简称。也有一些软件借用这个术语,指特定的可重用控件。
6)     构件:构件是系统中实际存在的可更换部分,它实现特定的功能,符合一套接口标准并实现一组接口。构件代表系统中的一部分物理实施,包括软件代码(源代码、二进制代码或可执行代码)或其等价物(如脚本或命令文件)。
通常认为,构件是特定软件开发环境中、满足指定规范的软件部分,其涵盖了设计、开发、物理实施等范畴。
7)     框架:框架的英文是Framework,框架是一个应用程序的半成品。框架提供了可在应用程序之间共享的可复用的公共结构。开发者把框架融入他们自己的应用程序,并加以扩展,以满足他们特定的需要。框架和工具包的不同之处在于,框架提供了一致的结构,而不仅仅是一组工具类。(摘自《JUnit in action中文版》)。
8)     子系统:子系统是个设计中使用的术语,英文是SubSystem。在软件总体架构中,按照功能划分子系统。通常一个子系统包含一个或多个进程。
9)     模块:模块是个设计中使用的术语,英文是Module。模块指一个子系统内部,按照软件结构分解的功能部分。模块会包含多个类、使用多个组件,也会与框架交互。
一个真正的软件系统,会涉及到以上的多个概念,典型的软件系统静态结构图如下:


上图展示了一个软件系统,包括2个子系统,子系统1和子系统2。子系统1调用子系统2。
子系统1包含2个模块:模块1和模块2。模块1由两个类(Class 1 和Class 2)和一个组件(组件1)构成,模块2由两个类(Class3和Class4)和两个组件(组件2和组件3)构成,模块2提供一个接口给模块1调用。模块1和模块2都使用了框架1。
子系统2包含2个模块:模块3和模块4。模块3由两个类(Class5和Class6)和一个组件(组件4)构成,模块4由一个类(Class7)和一个组件(组件5)构成。模块4提供一个接口给模块3调用。模块3和模块4都使用了框架2。[/img]..
经过慎重考虑,终于决定写一本书,书名暂时叫做《Java组件设计》,准备将自己多年的设计经验分享给大家。
本书主要面向软件架构师、设计师、高级开发人员,讲解企业应用中核心组件的设计原则与实践。
本书将澄清设计模式、数据结构、多线程、接口设计等多个领域中的常见误区,通过大量的实例分析,为读者精彩讲解组件设计这一最具技术含量的领域需要考虑的问题、设计方案与最佳实践。
 
章节目录,暂定如下:
第一章:组件设计概述
第二章:组件设计原则
第三章:预备知识
第四章:配置文件
第五章:Tcp通信组件
第六章..
2008-06-04 23:53:09
我的上一篇文章《KTree 组件需求规格》,将javascript 树型组件的功能规格定义了下来。我对树型组件的研究,从2周前就开始了,现在代码完成,发布出来,供大家使用。
先看一下效果:
1.  有根节点、有连线、各层树枝显示图片自定义:


2. 无根节点,无连线,各层树枝显示图片自定义:

3. 无连接线、显示CheckBox:

怎么样,不错吧。
下面讲一下怎么在页面上生成这棵树:
1. 引入ktree.js 和ktree.css 文件,这个大家都非常熟悉了:
<link rel="StyleSheet" href="ktree/ktree.css" type="text/css" />
 <script type="text/javascript" src="ktree/ktree.js"></script>
2. 确定KTree 要显示的位置,先把script的元素写上:
<script type="text/javascript">
<!--  我们要写的代码都在这里  -->
 
</script>
3. 创建树对象,如下:
    k = new KTree('k', 'myTree1', 'ktree/img/');
    KTree 函数原型为:
function KTree(objName, treeId, imgPath);
第一个参数:objName:此树对象实例的变量名,必需。就是”=”前面的那个k啊,这个变量必须是全局变量,因此变量前面不能有var。
第二个参数:treeId:树对象的Id,必需。可以任意命名,但名字需要唯一,这样可以支持一个页面有多个树。
    第三个变量:KTree 内置图片的存放目录,必需。连线、加减号等都是图片,因此需要指定存放位置,这样开发者就可以根据自己的目录规划,把KTree的图片放到指定的目录下。
4. 设置显示图片:
    k.setImageList('ktree/img/base.gif', 'ktree/img/folder.gif',
'ktree/img/folderopen.gif', 'ktree/img/globe.gif', 'table.png',
'shape_handles.png');
   setImageList 设置树枝和树叶显示时使用的各个图片,图片个数任意个。那么怎么指定树枝显示是哪个图片,树叶显示是哪个图片?见下面的函数:
    k.setDirImages(1, 2);
    k.setDirImages(4, 5, 1);
    setDirImages 函数是设置显示树枝的图片,函数原型如下:
    setDirImages = function(closeIndex, openIndex, dirLevel);
    参数如下:
    closeIndex : 树枝折叠时,显示图片在ImageList 数组中的索引下标,下标从0开始。
    openIndex:树枝展开时,显示图片在ImageList 数组中的索引下标,下标从0开始。
    dirLevel: 目录的层次,从0开始,0表示第一层树枝。可选参数,不填时表示默认层次,即没有专门指定图片的树枝层次,都统一使用默认层次指定的折叠和展开图片。
    setDirImages函数可以被多次调用,这样就可以为不同层次目录设置不同显示的图片。
设置树叶显示图片的方法:
setLeafImages = function(unselectedIndex, selectedIndex);
unselectedIndex : 树叶未被选中时显示的图片索引,从0开始。
selectedIndex:树叶被选中时显示的图片,可选参数。如不填,则一直显示unselectedIndex的图片。
5. 设置根节点显示:
   setRoot = function(rootId, rootName, imageIndex);
   设置根节点的参数,如下:
   rootId: 根节点的Id值。任何一个树节点,都至少有3个基本属性:id,pid,name. id 指应用程序中此对象的唯一编号(不是HTML Element ID),pid 指父节点的id。Name指显示的文本。此参数可选,缺省为-1。
    rootName: 根节点显示的文本,可选参数。如不提供此参数,表示不显示根节点。
    imageIndex: 根节点显示图片的索引下标。可选参数。
    如果不调用setRoot 函数,表示不显示根节点,树根的id 为 -1。
6. 设置显示风格:
   是否显示连线:k.showLine(false); 参数true显示连线,参数false不显示连线。如果不调用此函数,缺省显示连线。
    是否显示CheckBox:k.showCheckbox(true);参数true表示树叶节点将显示checkbox, 参数false则不显示。如果不调用此函数,缺省不显示checkbox。
7.增加树枝:
    k.addDir(1, -1, '营销资费管理', null, null, 'hypo3.html','mainFrame');
    k.addDir(2,1,'产品资费定价');
    addDir是增加树枝函数,原型如下:
    addDir = function(id, pid, name, icon, openIcon, url, target);
    参数如下:
    id : 此树枝节点id
    pid: 此树枝节点的父节点id。父节点必须在子节点前被加入到树中。如果pid == rootId, 表示此树枝是第1层树枝,dirLevel == 0.
    name : 树枝的显示文本。
   icon : 树枝折叠时显示的图片。有三种取值:1) null,表示使用setDirImages指定的图片;2) 图片索引下标,数字,表示用ImageList中指定下标的图片显示; 3) 图片全路径的字符串,表示用这个指定的图片来显示;
    openIcon : 树枝展开时显示的图片,取值方法与icon 相同。
    url:超链接指向的URL,可选
    target :超链接指向的target,可选
8.增加树叶:
    k.addLeaf(99,11,'Node 22');
    addLeaf 函数增加一个树叶,函数原型如下:
    addLeaf = function(id, pid, name, icon, openIcon, url, target);
    参数的取值,都与addDir 相同。增加树叶时,树叶的父节点必须已经被填加到树中。
9. 显示树:
   document.write(k);
   以上语句,其实是调用了KTree.toString() 方法,返回生成好的HTML元素,添加到document中,于是树就显示出来了。
10. 获得选中的节点:
如果树设置成显示checkbox, 那么用户可以多选,获得用户选中了哪些节点的方法是:
var nodes = k.getSelectNodes();
getSelectNodes() 函数返回的是一个数组,数组中每个成员都是一个Node 对象,Node对象的主要属性有id, pid, name, icon, openIcon, url, target,这些都非常简单。
 
以上,将KTree的使用方法讲解了一下,具体使用,请见我提供的test_ktree.html示例。  
 
 
 
 [/img]..
2008-06-02 23:50:35
前一段时间给客户做项目时,WEB界面上要提供很多树型结构。从网上找了一些源码,但发现要么是功能比较弱,根本不满足项目的要求,要么是功能很强,但代码很乱,难于看懂,也非常难改。
因此,就下决心自己写一个通用性强、可读性好的JavaScript树型组件。
树型组件名字定义为kTree,要支持的功能特性如下:
1.  同一页面上可以加载任意多个树,这些树的显示完全独立,互不影响;
2.  每个树枝和树叶的显示图形,都是可以用户自定义的,无需改动树型组件的代码和配置文件;
3.  树的层次深度不限制;
4.  树..
2008-04-22 22:16:55
     “不要重复发明轮子”,这句话伴随着Spring框架的日益广泛应用,而被广大开发人员所熟知,很多人奉为经典信条。
       然而,Bob Lee 重磅推出Guice,重复发明了一个轮子,挑战Spring框架,一下子在业界引起轩然大波。
        为什么要重复发明轮子呢?有着现成的开源框架不用,非要自己再写一个,费这劲干啥?这不是吃饱了撑的吗?
        任何一个读者,请试想一下,现在Java领域开源的框架、组..
       组件,是软件设计开发中实现可重用的重要手段。一个高度可重用性的组件,可以极大降低应用开发的难度,加快开发的速度,保障软件的质量。
       但是,不是任何的软件模块都可以叫做组件,也不是任何组件都能做到被后续开发过程高度重用。因此,这里需要对组件加以定义。
        在我看来,组件是指以源码或者二进制方式发布,能够在一个或多个业务/技术领域内,不做任何修改,被直接集成。组件具有如下特征:
 ..
       大型项目开发中,常用的数据库,当属Oracle。但Oracle 客户端安装就要一张光盘,体积很大。而且安装后,基本上就用2个功能:TNS配置服务名,SqlPlus。在开发过程中,大量使用Toad和PL/SQL Developer。因此,Oracle客户端安装盘,仅仅是被作为一个驱动而需要,根本没必要装那么大的空间。因此,本文给出了如何使用精简的Oracle客户端。

我使用两种精简的Oracle客户端,大家可以任选其一:
一、Oracle 9i (版本: 9.0.1.0.1)精简客户端 绿色版
        ..
类别:数据库|阅读(32064)|回复(10)|(5)阅读全文>>
2008-04-08 02:44:18
    这是一个组件泛滥的世界,并且愈演愈烈。
    在Java开源阵营里,各种框架、组件层出不穷,让人眼花缭乱,可以任由选择,却又无从选择。
    各种框架和组件体积日益庞大。Ant已经超过10M了,Struts 已经快50M了,Spring 已经突破80M了,而IBM做的软件,不超过2G都不好意思拿出来。好像组件越大,越能赢得开发人员的喜欢。
    可是,我们真的需要这么庞大的组件吗?我们真正需要这些组件做的事情,有多少?那么剩下的部分,对我们来讲,不就都是垃圾了吗?
 &n..
2008-04-08 01:48:59
    前人写过很多关于设计的文章,主要是从技术层面,讲设计模式、面向对象等等。今天,我从另外一个角度来讲讲。
    我们可以使用各种技术、模式、思想来进行软件设计,但是,用这么多的技术、模式、思想进行设计,我们的终极目的是什么?有人说:解耦。又有人说:可重用。还有人说:可扩展。这些听起来都象是答案,但都不是答案。因为,任何活动的目的,都是产生高质量的输出物,而这些输出物,将作为别人开展活动的输入。因此,评价你的活动是否达到目的,是应该看你的输出是否满足了别人的输入,..
       “井底之蛙”是个贬义词,指眼界狭小,固执认为天就是井口那么大。这个词一说大家就明白,但实际职场中,我接触的很多人都不同程度带有这个恶习,尤其是80后的新人,更为严重。
        因此,本文写给软件技术人员,让大家有则改之,无则加勉,端正心态,快速进步。
        软件,是个低门槛的行业。博士、硕士、本科、专科、高中生都可以写程序。尤其是VB、Delphi、Java、C#这些语言的兴起,更使得软件开发易于上手..
类别:随笔|阅读(1034)|回复(8)|(0)阅读全文>>
2008-03-24 00:23:05
    10年内,我面试过的技术人员,至少在1000人之上。因此,根据我的面试体会,想给技术人员提一些忠告。
    首先,面试被刷下来的人员,最多的,是因为基础功差,基本概念、基本原理不清楚。我在面试中考的基础题目,大部分都是大学学校里教的课程,比如数据结构、基本算法、语言基础,没有多少人清楚的。很多人在大学里面基本上就是玩,这些基本知识根本就没学会,并且很多人非常固执的认为,这些知识都过时了,做项目也根本用不到。很多工作5、6年,甚至7、8年的技术人员,连C语言中memcpy和memmov..
类别:随笔|阅读(3138)|回复(22)|(0)阅读全文>>
    一个软件的品质等级,只要看看软件界面,就马上能看出高下了。
    软件界面,首先是好用,其次才是好看。好多人经常把优先级弄错,先追求美观,用PS做很多图片,配上各种动态效果,但用起来非常难用,最后被客户骂个狗血喷头。
    软件以用为主,这句话非常深刻。软件是给人来用的,是用来解决某个领域内的问题的,完成相关的工作的。因此,能深刻理解客户的需求,提供给客户方便使用的界面,让客户能高效完成工作,这样的界面就是优秀的界面,能真正解决客户的问题。
&nb..
2008-03-21 00:12:58
     软件始于需求。
     软件开发,能否获得成功,最重要的是需求分析的工作。因此,软件需求分析师的能力和水平,对软件项目至关重要。
        多年以来,我接触过非常多软件从业人员,很多人说自己参加过多个项目,在项目组中做过需求分析,仔细聊起来却发现他们根本没有接受过软件需求分析的培训,对软件需求根本没有清晰的概念,甚至很多人认为把业务流程图画出来,把数据表做出来,就叫需求分析完成了。
      ..
Linux 下的应用软件,比Windows 下的各种商用软件,目前还是要差一些。从windows 转到Linux 下,明显感觉到软件的不顺手。其中之一就是下载软件:Windows 下迅雷下载速度最快,并且采用P2P,P2SP技术,极大加快了下载速度。
在我写这篇文章之前,网上已经有很多文章介绍在Linux下如何用wine 来运行迅雷,但是,这些讲的都是迅雷国际版,这个版本没有使用盗链技术,当下载的URL失效时,就无法再下载。这和Windows 下原生的迅雷,差了一大截。因此本文就是介绍在Linux 下如何运行原生的迅雷程序。

我的操作系统:ubuntu Gutsy 7.10
具体..
类别:ubuntu|阅读(20730)|回复(1)|(0)阅读全文>>
2008-03-16 18:40:08

在网上转了好长时间,终于决定开博。
先自我介绍一下:98年毕业,一直在电信领域做软件开发,从软件研发工程师到设计师、架构师、技术总监、研发总监都做过,自己最擅长软件需求分
析和架构设计,C++做了6年,java做了4年。注重基础知识、基本概念,学习必求深入理解内部原理,从本质上理解透彻。书本上的东西,一定要应用于项
目实践,从实践中来,再到实践中去,切实解决软件开发过程中的问题,而非追逐时尚和热炒。
自己计划的博客内容包括:
1. 软件需求分析
2. 架构设计
3. 代码实践
4. 软件项目管理
5. Linux
2008-03-16 16:38:30
写这篇文章的目的在于,网上提供的各种安装方法,实测后都无法成功。另外,本文提供的安装方法,可以成为一种常用的方法,用于安装各种Windows,Linux操作系统。

我的笔记本是ThinkPad T61, 已经安装了WinXP, 接下来说明如何硬盘方式安装ubuntu 7.10.


一、 为什么要安装ubuntu
     Linux 和Windows 有很大的区别,Linux是开源的,Linux下的各种软件,基本上都是开源的。这对我们深入学习有非常大的帮助。另外,在工作上,我所在的开发团队,开发的项目都是运行在Linux环境下的,因此自己的笔记本上有个Linux系..
类别:ubuntu|阅读(1151)|回复(2)|(0)阅读全文>>

公告