Java编程规范

来源:互联网 发布:私募 知乎 编辑:程序博客网 时间:2024/06/03 19:37

代码编写规范

目录1 前言1.1编写目的2 命名规范2.1Package的命名2.2Class的命名2.3Class变量的命名2.4接口的命名2.5参数的命名2.6数组的命名2.7方法的参数命名3 注释规范3.1实现注释的格式(Implementation Comment Formats)3.1.1 块注释(Block Comments)3.1.2 单行注释(Single-Line Comments)3.1.3 尾端注释(Trailing Comments)3.1.4 行末注释(End-Of-Line Comments)4 Java文件样式4.1版权信息4.2Package/Imports4.3Class4.4Class Fields4.5存取方法4.6构造函数4.7克隆方法4.8类方法4.9main 方法5 代码编写格式5.1代码样式5.2文档化5.3缩进5.4页宽5.5{}对5.6括号6 JAVA编码规范6.1注意6.2异常处理6.3垃圾收集6.4Clone6.5final 类6.6访问类的成员变量6.7编写类的公共问题7 JSP编码规范8 代码编译9 匈牙利命名规则9.1数据类型缩写9.2SWING一些控件的缩写9.3SWT一些控件的缩写9.4 方法10 部分编程常用单词缩写11 编程技巧11.1 byte数组转换到characters11.2 Utility 类11.3 初始化11.4 枚举类型11.5 byte[]中的值11.6 String和StringBuffer12 调试13 性能

前言
1.1 编写目的
本规范是本项目组开发人员必须遵循的,可供测试等其他人员参考,本规范只适合本项目组内部使用。
特别注意
1、所有需要等待的程序加上进度条;
2、所有的方法加上javadoc注释,重要变量和属性也加上注释说明;
3、代码必须进行格式化,以使代码可读性强;
2 命名规范
定义命名规范的目的是让项目中所有的文档看起来都像一个人写的,增加可读性,减少项目组中因为换人而带来的损失(这些规范并不是一定要绝对遵守,但是一定要让程序有良好的可读性)。


2.1 Package的命名
包名的后续部分根据不同机构、不同项目的命名规范而不尽相同,尽量清晰,易懂,容易理解。


2.2 Class的命名
Class 的名字必须由大写字母开头、而其他字母都小写的单词组成。尽量使类名简洁而有含义。
使用完整单词,避免缩写词(除非该缩写词被更广泛使用,像URL,HTML)。如:

class Raster、class ImageSprite。


2.3 Class变量的命名
除了变量名外,所有实例,包括类,类常量,均采用大小写混合的方式,第一个单词的首字母小写,其后单词的首字母大写。变量名不应以下划线或美元符号开头,尽管这在语法上是允许的。变量名应简洁而有含义。变量名的选用应该易于记忆,即能够指出其用途。尽量避免单个字符的变量名,除非是一次性的临时变量。临时变量通常被取名为i,j,k,m和n,它们一般用于整型;c,d,e,它们一般用于字符型。如:

char c、int i、float myWidth。

实例变量Instance Variables
大小写规则和变量名相似。
常量(Constants)类常量和ANSI常量的声明,应该全部大写,单词间用下划线隔开(尽量避免ANSI常量,容易引起错误)。如:
static final int MIN_WIDTH = 4、static final int MAX_WIDTH = 999、static final int GET_THE_CPU = 1。
除了以下几个特例之外,命名时应始终采用完整的英文描述符。此外,一般应采用小写字母,但类名、接口名以及任何非初始单词的第一个字母要大写。
1、尽量使用完整的英文描述符;
2、采用适用于相关领域的术语;
3、采用大小写混合使名字可读;
4、尽量少用缩写,但如果用了,要明智地使用,且在整个工程中统一;
5、避免使用长的名字(小于 15 个字母);
6、避免使用类似的名字,或者仅仅是大小写不同的名字;
7、避免使用下划线。


2.4 接口的命名
大小写规则与类名相似,如:
interface RasterDelegate、interface Storing。 
方法(Methods) 名采用大小写混合的方式,第一个单词的首字母小写,其后单词的首字母大写。如:

run()、runFast()、getBackground()。

2.5 参数的命名
参数的名字必须和变量的命名规范一致。


2.6 数组的命名
数组应该总是用下面的方式来命名:
byte[] buffer;
而不是:
byte buffer[];


2.7 方法的参数命名
使用有意义的参数命名,如果可能的话,使用和要赋值的字段一样的名字: 

SetCounter(int size){this.size = size;}

3 注释规范
一般情况下程序有两类注释:实现注释(Implementation Comments)和文档注释(Document Comments)。实现注释是使用/*...*/和//界定的注释。文档注释(被称为"doc comments")是Java独有的,并由/**...*/界定。文档注释可以通过javadoc工具转换成HTML文件。
实现注释用以注释代码或者实现细节。文档注释从实现自由(implementation-free)的角度描述代码的规范。它可以被那些手头没有源码的开发人员读懂。注释应被用来给出代码的总括,并提供代码自身没有提供的附加信息。注释应该仅包含与阅读和理解程序有关的信息。例如,相应的包如何被建立或位于哪个目录下之类的信息不应包括在注释中。


3.1 实现注释的格式(Implementation Comment Formats)
程序可以有4种实现注释的风格:块(block)、单行(single-line)、尾端(trailing)和行末(end-of-line)。


3.1.1 块注释(Block Comments)
块注释通常用于提供对文件、方法、数据结构和算法的描述。块注释被置于每个文件的开始处以及每个方法之前。它们也可以被用于其他地方,比如方法内部。在功能和方法内部的块注释应该和它们所描述的代码具有一样的缩进格式。
块注释之首应该有一个空行,用于把块注释和代码分割开来,比如:

/** Here is a block comment.*/块注释可以以/*-开头,这样indent(1)就可以将之识别为一个代码块的开始,而不会重排它。/*-* Here is a block comment with some very special* formatting that I want indent(1) to ignore.** one* two* three*/

3.1.2 单行注释(Single-Line Comments)
短注释可以显示在一行内,并与其后的代码具有一样的缩进层级。如果一个注释不能在一行内写完,就该采用块注释。单行注释之前应该有一个空行。以下是一个Java代码中单行注释的例子:

if (condition) {/* Handle the condition. */...}

3.1.3 尾端注释(Trailing Comments)
极短的注释可以与它们所要描述的代码位于同一行,但是应该有足够的空白来分开代码和注释。若有多个短注释出现于大段代码中,它们应该具有相同的缩进。
以下是一个Java代码中尾端注释的例子:

if (a == 2) {return TRUE; /* special case */} else {return isPrime(a); /* works only for odd a */}

3.1.4 行末注释(End-Of-Line Comments)
注释界定符"//",可以注释掉整行或者一行中的一部分。它一般不用于连续多行的注释文本;然而,它可以用来注释掉连续多行的代码段。以下是所有三种风格的例子:

if (foo > 1) {// Do a double-flip....}else {return false; // Explain why here.}//if (bar > 1) {//// // Do a triple-flip.// ...//}//else {// return false;//}

4 Java文件样式
注:如果用ezStudio,可以导入我们组约定的代码模板文件后,就自动定义好了文件样式!
所有的 Java(*.java) 文件都必须遵守如下的样式规则。


4.1 版权信息
版权信息必须在 java 文件的开头,比如:
/*
*/
其他不需要出现在 javadoc 的信息也可以包含在这里。


4.2 Package/Imports
package行要在import行之前,import 中标准的包名要在本地的包名之前,而且按照字母顺序排列。
package com.thtf.ezone.……;


import java.util.Observable;
import hotlava.util.Application;

4.3 Class
接下来的是类的注释,一般是用来解释类的。

/** * <p>描述: Jpc服务的用户接口类,调用了JpcService类的实现。 * </p> * @author作者姓名 * @version 3.1 */

接下来是类定义,包含了在不同的行的extends和implements。

public class CounterSetextends Observableimplements Cloneable

4.4 Class Fields
接下来是类的成员变量:
/** * Packet counters */protected int[] packets;

public 的成员变量必须生成文档(JavaDoc)。proceted、private和 package 定义的成员变量如果名字含义明确的话,可以没有注释。


4.5 存取方法
接下来是类变量的存取的方法。它只是简单的用来将类的变量赋值获取值的话,可以简单的写在一行上。如类的成员变量已经有注释,类变量的存取方法可以没有注释。

/** * 功能说明: … * @param opcData  要设置的Opc数据 * @return true 成功 false 失败 * @throws ServiceUnavailableException 服务不可用异常*/public static boolean setDataToOpc(OpcData opcData) throwsServiceUnavailableException {return true;}public int[] getPackets(String s) { return copyArray(packets, offset); }public int[] getBytes() { return copyArray(bytes, offset); }public int[] getPackets() { return packets; }public void setPackets(int[] packets) { this.packets = packets; }

要求说明的是,对于集合,加入成员函数来插入和删除项;另其它的方法不要写在一行上。


4.6 构造函数
接下来是构造函数,它应该用递增的方式写(比如:参数多的写在后面)。访问类型 (“"public”,“private”等) 和任何“static”,“final”或“synchronized” 应该在一行中,并且方法和参数另写一行,这样可以使方法和参数更易读。

publicCounterSet(int size){  this.size = size;}

4.7 克隆方法
如果这个类是可以被克隆的,那么下一步就是 clone 方法:
publicObject clone() {  try {    CounterSet obj = (CounterSet)super.clone();    obj.packets = (int[])packets.clone();    obj.size = size;    return obj;  }catch(CloneNotSupportedException e) {    throw new InternalError("Unexpected CloneNotSUpportedException: " + e.getMessage());  }}

4.8 类方法
下面开始写类的方法:
/** * Set the packet counters * (such as when restoring from a database) */protected finalvoid setArray(int[] r1, int[] r2, int[] r3, int[] r4)  throws IllegalArgumentException{  //  // Ensure the arrays are of equal size  //  if (r1.length != r2.length || r1.length != r3.length || r1.length != r4.length)throw new IllegalArgumentException("Arrays must be of the same size");  System.arraycopy(r1, 0, r3, 0, r1.length);  System.arraycopy(r2, 0, r4, 0, r1.length);}

4.9 main 方法
如果main(String[]) 方法已经定义了,那么它应该写在类的底部。

5 代码编写格式
5.1 代码样式
代码应该用 unix 的格式,而不是 windows 的(比如:回车变成回车+换行)。


5.2 文档化
必须用 javadoc来为类生成文档。不仅因为它是标准,这也是被各种 java 编译器都认可的方法。


5.3 缩进
缩进应该是每行4个空格或者一个Tab字符。注意:在使用不同的源代码管理工具时,Tab字符将因为用户设置的不同而扩展为不同的宽度。


5.4 页宽
页宽应该设置为80字符。源代码一般不会超过这个宽度,并导致无法完整显示,但这一设置也可以灵活调整。在任何情况下,超长的语句应该在一个逗号或者一个操作符后折行。一条语句折行后,应该比原来的语句再缩进4个字符。


5.5 {}对
{} 中的语句应该单独作为一行。例如,下面的第1行是错误的,第2行是正确的:

if (i>0) { i ++ }; // 错误, { 和 } 在同一行if (i>0) { i ++ };       // 正确, } 单独作为一行
} 语句永远单独作为一行


5.6 括号
左括号和后一个字符之间不应该出现空格,同样,右括号和前一个字符之间也不应该出现空格。下面的例子说明括号和空格的错误及正确使用:
CallProc( AParameter ); // 错误
CallProc(AParameter); // 正确
不要在语句中使用无意义的括号。括号只应该为达到某种目的而出现在源代码中。下面的例子说明错误和正确的用法:
if ((I) = 42) {   // 错误 - 括号毫无意义
if (I == 42) or (J == 42) then   // 正确 - 的确需要括号


6 JAVA编码规范
6.1 注意
1、每个类不能长于1000行,一个方法的长度尽量控制在50行内;
2、每个类、每个方法都必须都有注释,在一个方法内长于10行必须要有注释;
3、一个方法只能有一项明确的责任;
4、让一切东西都尽可能地“私有”——private,除非有特别好的理由,不要把任何类变量或实例变量公众化(public)。我们规定,实例变量是不需要显式的设置或获取的,统一设成私有的,要取则必须通过某种方法来完成。
5、尽量使用interfaces,不要使用abstract类。
6、避免代码重复。
7、import类时不要使用通配符,如import java.awt.*,要把每一个用到的类单独列出,没有用到的一定要去除。
8、exit 除了在main中可以被调用外,其他的地方不应该调用。因为这样做不给任何代码代码机会来截获退出。一个类似后台服务的程序不应该因为某一个库模块决定了要退出就退出。


6.2 异常处理
申明的错误应该抛出一个RuntimeException或者派生的异常。
顶层的main()函数应该截获所有的异常,并且打印(或者记录在日志中)在屏幕上。
异常处理要根据类型进行编码,如“ezStudio3-0001”
在错误输出时采用错误编码+提示信息的方式,如:" ezStudio3-0001:文件打开失败",其中提示信息从resourceBundle中读出。
错误的输出采用Log4j来实现。即输出时不要采用System.out.println("ezStudio30001:打开文件失败")的方式,而要用logger.warn("ezIBS0001:打开文件失败")的方式。


6.3 垃圾收集
JAVA使用成熟的后台垃圾收集技术来代替引用计数。但是这样会导致一个问题:必须在使用完对象的实例以后进行清场工作。我建议大家能尽可能地由我们自己手工清除垃圾,收集内存。
除非输出流一出作用域就关闭,非引用计数的程序语言,比如JAVA,是不能自动完成变量的清场工作的。必须象下面一样写:

FileOutputStream fos = new FileOutputStream(projectFile);project.save(fos, "IDE Project File"); fos.close();

6.4 Clone
下面是一种有用的方法:
  implements Cloneable  public    Object clone()    {      try {        ThisClass obj = (ThisClass)super.clone();        obj.field1 = (int[])field1.clone();        obj.field2 = field2;        return obj;      } catch(CloneNotSupportedException e) {        throw new InternalError("Unexpected CloneNotSUpportedException: " + e.getMessage());      }  }

6.5 final 类
绝对不要因为性能的原因将类定义为 final 的(除非程序的框架要求)。
如果一个类还没有准备好被继承,最好在类文档中注明,而不要将她定义为 final 的。这是因为没有人可以保证会不会由于什么原因需要继承它。


6.6 访问类的成员变量
大部分的类成员变量应该定义为 protected 的来防止继承类使用他们。
注意,要用"int[] packets",而不是"int packets[]",后一种永远也不要用。
public void setPackets(int[] packets) { this.packets = packets; }
CounterSet(int size)
{
this.size = size;
}


6.7 编写类的公共问题
1、为了常规用途而创建一个类时,请采取“经典形式”,并包含对下述元素的定义:
equals()   
hashCode()   
toString()   
clone()(implement Cloneable)   
implement Serializable  
2、对于自己创建的每一个类,都考虑置入一个main(),其中包含了用于测试那个类的代码。为使用一个项目中的类,我们没必要删除测试代码。若进行了任何形式的改动,可方便地返回测试。这些代码也可作为如何使用类的一个示例使用。
3、让一切东西都尽可能地“私有”——private。可使库的某一部分“公共化”(一个方法、类或者一个字段等等),就永远不能把它拿出。若强行拿出,就可能破坏其他人现有的代码,使他们不得不重新编写和设计。若只公布自己必须公布的,就可放心大胆地改变其他任何东西。在多线程环境中,隐私是特别重要的一个因素——只有private字段才能在非同步使用的情况下受到保护。
4、尽量使用interfaces,不要使用abstract类。若已知某样东西准备成为一个基础类,那么第一个选择应是将其变成一个interface(接口)。只有在不得不使用方法定义或者成员变量的时候,才需要将其变成一个abstract(抽象)类。接口主要描述了客户希望做什么事情,而一个类则致力于(或允许)具体的实施细节。
7 JSP编码规范
1、整个jsp/jsp bean表示层应当尽可能的瘦和简单化。
2、牢记大多数的JSP都应当是只读的视图,而由页面bean来提供模型。
3、应当一起设计JSP和JSP bean。
4、在尽可能合理的情况下,把业务逻辑从JSP中移走。具体于HTTP的逻辑(如,对Cookie的处理)属于bean或支持类中,而不是JSP中。
5、尽量把条件逻辑放在控制器中而不是放在视图中。
6、为JSP、包含的文件、JSP Bean和实现扩展标记的类使用遵循标准的命名惯例。如:
jsp控制器 : xxxxController.jsp
被包含的 : jsp _descriptiveNameOfFragment.jsp
jsp页面模型bean : <pagename>Bean 如loginBena.java
jsp会话bena : xxxxSessionBean
标记类: xxxxTag,xxxxTagExtraInfo
7、应当在JSP中避免使用页面引入指令。import指令会促使类的实列化而不是jsp bean的实例化:
不用:<%@ page import = "com.java.util.*" %>
而用:<% java.util.List l = new java.util.LinkedList(); %>
8、jsp不应该直接去访问请求参数。bean应当执行这样的处理过程并且输出所处理的模型数据。
9、jsp不应当访问属性文件,或者使用JNDI。bean可以访问属性。
10、如果jsp bean具有的所有的属性不能够从页面请求中被映射到,就要尽力在<jsp:useBean>标记中设置属性。
11、应当避免设计既显示表单又处理结果的页面。
12、在jsp中避免代码重复。把要重复的功能放在一个包含的jsp、bean或标记扩展中,使得它能够被重用。
13、jsp bean应当永远不要去产生HTML。
14、在jsp中应该避免使用out.pringln()发放来产生页面内容。
15、jsp层不应该直接访问数据,这包括JDBC数据库访问和EJB访问。
16、在长度上,scriptlests的代码最好不要超过5行。
17、除了jsp bean之外,jsp不应当去实例化复杂的可读写的对象。如果这样的话,就有可能在jsp中去执行不适当的业务逻辑。
18、jsp bean中不应当包含大量的数据。
19、如果使用了<jsp:forward>和<jsp:include标记>,并且必须使用简单类型的值来与外部页面进行通讯的话,就应当使用一个或多个<jsp:param>元素。
20、定制标记应当用在适当把逻辑从jsp中移走的地方。
21、应当谨慎地使用<jsp:forward>标记,在jsp中它是一个等价的goto。
22、应当使用隐藏的注释来阻止输出的HTML过大。
23、在jsp中避免进行异常处理。
24、每个jsp文件中都应当使用一个错误页面来处理不能够从中恢复的异常。
25、在jsp错误页面中,使用HTML注释来显示传递到该页面中的异常跟踪信息。
26、只有在能够获得性能上的好处时,才使用jspInin()方法和jspDestroy()方法。获取和放弃资源是jsp beans和标记处理器的事,而不是由jsp来负责的。
27、如果没有充分的理由,就不要在jsp中定义方法和内部内。


8 代码编译
1、编写代码时要注意随时保存,并定期备份,防止由于断电、硬盘损坏等原因造成代码丢失。
2、同一项目组内,最好使用相同的编辑器,并使用相同的设置选项。
3、合理地设计软件系统目录,方便开发人员使用。
4、打开编译器的所有告警开关对程序进行编译。
5、在同一项目组或产品组中,要统一编译开关选项。
6、使用工具软件(如SVN)对代码版本进行维护。


9 匈牙利命名规则
每个系统应尽量遵循一种命名规则,目的是为了代码风格的统一,匈牙利命名法是诸多系统均采用的方法。
9.1 数据类型缩写

前缀 数据类型b或is/bObject boolean/Boolean                                                 by/byObject byte/Byte                                                    char或c/cObject char/Char      int或i/iObject integer/Integer  str或/s/sObject String  d/dObject double/Doublef/fObject float/Floatl/lObject long/Longv Vectorm Map

9.2 SWING一些控件的缩写
前缀    控件名称jcmb/cmb JComboBox/ComboBox    jedt/edt JEdit/Edit        jdlg/dlg JDialog/Dialog      jlst/lst JListBox/ListBox     jbtn/btn JButton/Button        jtxt/txt JText/Text          jchk/chk JCheckBox/CheckBox 



9.3 SWT一些控件的缩写
Text、Label、Button、Combo、Table、TableItem
可以用名词在前对象名在后的方式,如:
newNameText 代表一个Text
saveButton代表一个Button
也可以将对象名放在前后面加下划线再加名词的方式,如:
text_NewName 代表一个Text
button_Save代表一个Button
一般变量的命名规则遵循能够表达出语义就可以,如length/index均可以表示一个int类型的变量,总之要使变量在上下文可以清楚明了的表达其实际含义。


9.4 方法
前缀 修饰符
v Void                 
fn Funtion            
10 部分编程常用单词缩写
规则:较短的单词可通过去掉“元音”形成缩写;较长的单词可取单词的头几个字母形成缩写;一些单词有大家公认的缩写。

完整单词缩写Aaverageavg  ;Bbackbk   ;backgroundbg   ;breakbrk  ;bufferbuf  ;Ccolorcr / (clr)controlctrl ;Ddatadat  ;deletedel  ;documentdoc  ; Eeditedt  ;errorerr  ;escapeesc  ;Fflagflg  ;formfrm  ;Ggridgrd  ;Iincrementinc  ;informationinfo ;initialinit ;insertins  ;imageimg  ;Llabellab  ;lengthlen  ;listlst  ;librarylib  ;Mmanagermngr/ (mgr)messagemsg  ;OOracleOra  ;Ppanoramapano ;passwordpwd  ;picturepic  ;pointpt   ;positionpos  ;printprn  ;programprg  ;Sserversrv  ;sourcesrc  ;statisticstat ;stringstr  ;Sybasesyb  ;Ttemptmp  ;texttxt  ;Uuserusr  ;Wwindowwnd  / (win)

11 编程技巧
11.1 byte数组转换到characters
为了将 byte数组转换到characters,可以这么做:
"Hello world!".getBytes(); 


11.2 Utility 类 
Utility 类(仅仅提供方法的类)应该被申明为抽象的来防止被继承或被初始化。 


11.3 初始化 
下面的代码是一种很好的初始化数组的方法:
objectArguments = new Object[] { arguments }; 


11.4 枚举类型
JAVA 对枚举的支持不好,但是下面的代码是一种很有用的模板:
class Colour {  public static final Colour BLACK = new Colour(0, 0, 0);  public static final Colour RED = new Colour(0xFF, 0, 0);  public static final Colour GREEN = new Colour(0, 0xFF, 0);  public static final Colour BLUE = new Colour(0, 0, 0xFF);  public static final Colour WHITE = new Colour(0xFF, 0xFF, 0xFF);}
这种技术实现了RED, GREEN, BLUE 等可以象其他语言的枚举类型一样使用的常量。他们可以用 '==' 操作符来比较。
但是这样使用有一个缺陷:如果一个用户用这样的方法来创建颜色 BLACK
new Colour(0,0,0) 
那么这就是另外一个对象,'=='操作符就会产生错误。她的 equal() 方法仍然有效。由于这个原因,这个技术的缺陷最好注明在文档中,或者只在自己的包中使用。


11.5 byte[]中的值
从文件中读取512个字节到byte[] ba;byte应该是0到255,但是在直接转换为int类型的时候出现了负值。用int i = ba[1]&0xff;


11.6 String和StringBuffer
前者是不可变的,后者是可变的。对于可能变化的字符串,一律采用StringBuffer。传入函数的字符串,如果还要传出结果,必须采用后者,即StringBuffer。


12 调试
调试在软件开发中是一个很重要的部分,存在软件生命周期的各个部分中。调试能够用配置开、关是最基本的。
对调试信息的输出,我们统一用Log4j来控制。


13 性能
1、在写代码的时候,从头至尾都应该考虑性能问题。这不是说时间都应该浪费在优化代码上,而是我们时刻应该提醒自己要注意代码的效率。比如:如果没有时间来实现一个高效的算法,那么我们应该在文档中记录下来,以便在以后有空的时候再来实现。
不是所有的人都同意在写代码的时候应该优化性能这个观点的,他们认为性能优化的问题应该在项目的后期再去考虑,也就是在程序的轮廓已经实现了以后。
2、不必要的对象构造
(1)不要在循环中构造和释放对象;
(2)使用 StringBuffer 对象。
在处理 String 的时候要尽量使用 StringBuffer 类,StringBuffer 类是构成 String 类的基础。String 类将 StringBuffer 类封装了起来(以花费更多时间为代价),为开发人员提供了一个安全的接口。当我们在构造字符串的时候,我们应该用StringBuffer来实现大部分的工作,当工作完成后将StringBuffer对象再转换为需要的String对象。比如:如果有一个字符串必须不断地在其后添加许多字符来完成构造,那么我们应该使用StringBuffer对象及其append()方法。如果我们用String对象代替StringBuffer对象的话,会花费许多不必要的创建和释放对象的 CPU 时间。
3、避免太多的使用 synchronized 关键字
避免不必要的使用关键字synchronized,应该在必要的时候才使用,这是一个避免死锁的好方法。


附上文档:http://download.csdn.net/detail/xgx120413/9347801

1 0
原创粉丝点击