`
qdpurple
  • 浏览: 276671 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

我需要培养的编程风格

阅读更多

本文分析了jdk源代码String.class 的代码规范.

 

   接触java有两年多了,真正的开发也只有不到3个月. 看平台代码才知道,自己的差距还是很远的. 各式各样的风格都有,这是与好的程序员的差距.从今天开始,我要树立自己的代码风格,模板为jdk源代码. 一些基本的规范就不列举了.

 

1.空格

(1) 关于=号, 对变量赋值时,按照jdk源码 = 两端加两个空格,如

int size = original.count;

 (2)对于关系运算符(> < == ..) 两端也加空格

if (offset < 0) {

  (3) 方法后 加空格然后{

  public String() {

 (4)对于多个参数间,参数间逗号后加空格

	System.arraycopy(value, 0, v, 0, size);

 (5) if语句中 括号() 外面两侧都加一个空格

if (offset < 0) {

 2 空行

(1)pakage上下各一空行

(2)所有import中没有空行

(3)成员变量间有一空行

(4)连个方法间有一空行

 

3.注释

(1)类注释:写出类的功能, 其它格式见下,如author, 日期格式: MM/DD/YYYY

/**
 * The <code>String</code> class represents character strings. All
 *
 * @author  Lee Boynton
 * @author  Arthur van Hoff
 * @version 1.189, 10/21/05
 * @see     java.lang.Object#toString()
 * @see     java.lang.StringBuffer
 * @see     java.lang.StringBuilder
 * @see     java.nio.charset.Charset
 * @since   JDK1.0
 */

 (2)成员变量的注释:单行使用/** comment .*/ ( 注意标点)

  /** The value is used for character storage. */
    private final char value[];

 (3)方法注释: 首行写出方法作用,

 参数行通过@param 参数变量 参数注释.

异常 @exception 异常名 注释

返回  @return 注释

 /**
     * Allocates a new <code>String</code> that contains characters 
     * @param      value    array that is the source of characters.
     * @param      offset   the initial offset.
     * @param      count    the length.
     * @exception  IndexOutOfBoundsException  if the 
     *        the bounds of the <code>value</code> array.
     */
    public String(char value[], int offset, int count) {
  

 (5)单行注释

//和下面if 竖直对齐

        // Argument is a String
        if (cs.equals(this))

 

分享到:
评论
51 楼 mfkvfn 2011-02-18  
果然是新手帖子。

你说的那些都只要Ctrl+Shift+F格式化一下就可以,也不一定一成不变,不同的公司或团队都有自己的代码规范。别人设置好了导出成xml给你你导入一下,然后写代码后Ctrl+shift+F就行了(当然也可以设置成保存时自动格式化代码)。
50 楼 抢街饭 2011-02-18  
编程风格并不同于其他的style,目的并不是形成自己独树一帜的风格,而是为了让你的程序易读,有利于团队合作以及别人帮你改错。

49 楼 xieyanhua 2011-02-18  
编码风格一帮通过IDE格式就可以了,在加上一些项目的一些约定,编码风格不是问题,
问题是你的代码注释够不够,在该有注释的地方有没有!

我自认为我的编码风格和注释习惯都非常好,我项目的部分代码注释覆盖率能达到75%,平均覆盖率有50%-60%,
我的编码风格,文件头有版权等注释信息,类名上有类的功能说明注释,所有成员变量均有注释其功能,所有方法均有注释说明,方法中,一些代码我们自己都比较难理解,绝对有注释,甚至是一行一个注释;有些代码我知道,但是我认为别人在读我的程序的时候,有可能不明白,我也会加上注释;对于一个功能,设计多个步骤,我的注释会一步一步的注释如:
   // 1、第一步骤做什么
   
   // 1-1、1-2 做说明,如果有
   //2 、第二步做什么
   //3 、第三步做什么................
还有就是要不断的重构,我的代码中,几乎很少有超过60行的一个方法,除非刚开始写的,没有重构过的代码,只要有超过的,当然不是绝对,95%我都会提炼出来,独立为一个方法。

实例代码(有些不合理,如果代码中的一些常量没有定义为常量,是因为项目还没有规划好异常信息代码的分类等,所以直接写在代码中等):实例代码不代表我的全部,有些地方不一定要注释,有时候也会被人误解为注释过度,但是我认为,有注释总比没有注释好,很多同学,注释是被迫的,觉得不需要或增加他的工作量,但是我认为注释是我的一个好习惯,我一直是边写代码边注释的,只要我觉得有必要加注释的,我就会毫不吝啬的写上代码,而且是让人能看懂的注释;很多同学不喜欢注释,或写代码的时候不只是,结果后面项目要求了,让他不注释,就不愿意了,或者应付式注释。如果你养成了一种好习惯,就不会被动了!

/**
 * FrontUserInfoServiceImpl.java V1.0 2010-4-1 上午09:06:00
 * 
 * Copyright 2010 Shenzhen **** Information Technology Co.,Ltd. All rights reserved.
 * 
 * Modification history(By Time Reason):
 * 
 * Description:
 */

package com.****.service.user.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.****.dao.user.AccumulatePointsDAO;
import com.****.dao.user.FrequentFlyerInfoDAO;
import com.****.dao.user.PortalUserInfoDAO;
import com.****.dao.user.VoucherDAO;
import com.****.model.front.user.FrequentFlyerModel;
import com.****.model.front.user.FrontUserInfoModel;
import com.****.service.user.FrontUserInfoService;
import com.****.usermodel.front.user.PasswordDTO;
import com.****.usermodel.front.user.PointsSummaryDTO;
import com.****.usermodel.front.user.VoucherSummaryDTO;
import com.****.utils.GlobalKeys;
import com.****.utils.factory.DAOFactory;
import com.****.frame.Service.BaseServcie;
import com.****.frame.exception.GeneralException;
import com.****.util.charater.StringUtil;
import com.****.util.collection.CollectionUtil;
import com.****.util.encrypt.DesEncrypt;

/**
 * <b>功能描述:</b>用户信息Service操作实现类
 * 
 * @author :****(Kevin.xie)<br>
 * 
 *         <b>修改历史:</b>(修改人,修改时间,修改原因/内容)<br>
 */
public class FrontUserInfoServiceImpl extends BaseServcie implements FrontUserInfoService {
    
    /**
     * 常旅客-人员DAO
     */
    private FrequentFlyerInfoDAO frequenterDAO;
    
    /**
     * 会员积分DAO
     */
    private AccumulatePointsDAO accumulatePointsDAO;
    
    /**
     * 优惠券DAO
     */
    private VoucherDAO voucherDAO;
    
    /**
     * @author :****(Kevin.xie)
     * 
     * @param userName 用户名
     * @return 用户信息对象
     * 
     * <br>
     *         (non-Javadoc)
     * @see com.****.service.user.FrontUserInfoService#getUserInfoByUserName(java.lang.String)
     */
    @Override
    public FrequentFlyerModel getUserInfoByUserName(String userName) {

        getFrequentFlyerInfoDAO();
        
        FrequentFlyerModel userInfoModel = null;
        
        // 1、获取指定用户的用户信息
        List<FrequentFlyerModel> list = frequenterDAO.getFrequentersListByUserName(userName, true);
        
        // 2、如果有记录,必然是只有条记录
        if (!CollectionUtil.listIsNullOrEmpty(list)) {
            
            userInfoModel = list.get(0);
        }
        
        return userInfoModel;
    }
    
    /**
     * @author :****(Kevin.xie)
     * 
     * @param frequenterModel 要修改的用户信息模型
     * @param userName 用户名
     * @return 包含用户基本信息、积分信息和优惠券等信息的集合
     * 
     * <br>
     *         (non-Javadoc)
     * @see com.****.service.user.FrontUserInfoService#modifyUserInfo
     * (com.****.model.front.user.FrequentFlyerModel,
     *      java.lang.String)
     */
    @Override
    public List<Object> modifyUserInfo(FrequentFlyerModel frequenterModel, String userName) {

        // 1、修改用户信息
        getFrequentFlyerInfoDAO();
        frequenterDAO.modifyFrequenter(frequenterModel, userName);
        
        // 2、获取用户的详细信息
        return getDetailsUserInfo(userName);
    }
    
    /**
     * 
     * @author :****(Kevin.xie)
     * 
     * @param userName 用戶名
     * @return 包含用户的用户信息,积分信息和优惠券信息的集合
     * 
     * <br>
     *         (non-Javadoc)
     * @see com.****.service.user.FrontUserInfoService#getDetailsUserInfo(java.lang.String)
     */
    public List<Object> getDetailsUserInfo(String userName) {

        List<Object> retList = null;
        
        if (StringUtil.isNEIgnoreWhiteSpace(userName)) {
            
            return retList;
        }
        
        // 1、个人信息
        FrequentFlyerModel frequentFlyer = null;
        retList = new ArrayList<Object>();
        
        getFrequentFlyerInfoDAO();
        List<FrequentFlyerModel> frequenterList = frequenterDAO.getFrequentersListByUserName(
                userName, true);
        if (!CollectionUtil.listIsNullOrEmpty(frequenterList)) {
            
            frequentFlyer = frequenterList.get(0);
        }
        
        // 2、可用积分、最近一年分和总历史积分信息对象
        getAccumulatePointsDAO();
        PointsSummaryDTO pointsSummary = accumulatePointsDAO.getPointsSummary(userName);
        
        // 3、优惠券列表信息
        getVoucherDAO();
        List<VoucherSummaryDTO> voucherList = voucherDAO.getVoucherSummary(userName);
        
        // 4、组装数据返回
        retList.add(frequentFlyer);
        retList.add(pointsSummary);
        retList.add(voucherList);
        
        return retList;
    }
    
    /**
     * @author :****(Kevin.xie)
     * 
     * @param passwordDTO 包含账户密码信息对象
     * @return 用户的详细信息,包括用户的用户信息,积分信息和优惠券信息
     * 
     * <br>
     *         (non-Javadoc)
     * @see com.****.service.user.FrontUserInfoService#changePassword
     * (com.****.usermodel.front.user.PasswordDTO)
     */
    @Override
    public List<Object> changePassword(PasswordDTO passwordDTO) {

        if (passwordDTO == null) {
            
            throw new GeneralException("1000001", "更新密码的数据丢失");
        }
        
        // 1、判断用户是否存在或密码是否正确
        String oldPasswordEncrypt = DesEncrypt.imEncrypt(passwordDTO.getOldPassword());
        PortalUserInfoDAO userInfoDAO = DAOFactory.getPortalUserInfoDAO();
        FrontUserInfoModel userInfoModel = userInfoDAO.portalLogon(passwordDTO.getUserName(),
                oldPasswordEncrypt);
        
        if (userInfoModel == null) {
            
            throw new GeneralException("1000001", "密码或用户名不匹配");
        }
        
        // 2、设置修改数据
        String encryptPwd = DesEncrypt.imEncrypt(passwordDTO.getNewPassword());
        userInfoModel.setPassword(encryptPwd);
        userInfoModel.setModificator(GlobalKeys.TAL_MODIFICATOR_FIELD);
        userInfoModel.setMoDate(new Date());
        userInfoModel.setMoUnit(GlobalKeys.TAL_MO_UNIT_FIELD);
        
        // 3、更新数据库
        userInfoDAO.modifyUserInfo(userInfoModel, passwordDTO.getUserName());
        
        // 4、获取用户的详细信息
        return getDetailsUserInfo(passwordDTO.getUserName());
    }
    
    /**
     * 
     * <b>功能描述:</b>获取FrequentFlyerInfoDAO的实例
     * 
     * @author :****(Kevin.xie) <br>
     *         <b>创建日期 :</b>2010-3-22 下午09:14:38
     * 
     * 
     * <br>
     *         <b>修改历史:</b>(修改人,修改时间,修改原因/内容)<br>
     */
    private void getFrequentFlyerInfoDAO() {

        frequenterDAO = DAOFactory.getFrequentFlyerInfoDAO();
    }
    
    /**
     * 
     * <b>功能描述:</b>获取AccumulatePointsDAO的实例
     * 
     * @author :****(Kevin.xie) <br>
     *         <b>创建日期 :</b>2010-3-31 上午10:45:47
     * 
     * 
     * <br>
     *         <b>修改历史:</b>(修改人,修改时间,修改原因/内容)<br>
     */
    private void getAccumulatePointsDAO() {

        accumulatePointsDAO = DAOFactory.getAccumulatePointsDAO();
    }
    
    /**
     * 
     * <b>功能描述:</b>获取VoucherDAO的实例
     * 
     * @author :****(Kevin.xie) <br>
     *         <b>创建日期 :</b>2010-3-31 下午02:48:14
     * 
     * 
     * <br>
     *         <b>修改历史:</b>(修改人,修改时间,修改原因/内容)<br>
     */
    private void getVoucherDAO() {

        voucherDAO = DAOFactory.getVoucherDAO();
    }
}
48 楼 lucane 2011-02-18  
li2005 写道
我还是赞同楼主养成好的编码习惯,在有些情况下,我不赞同楼上几们说用ctrl+shift+f来格式化代码。因为现在的项目基本都有版本管理软件,如SVN,有时一个类,自己根本就没有修改,只是用ctrl+shift+f来格式化代码,这就会导致SVN认为这一个版本的类和上一个版本的类有很大的不同

协同开发就选中自己的代码块,然后CTRL+SHIFT+F
47 楼 yidao620c 2011-02-17  
lyb520320 写道
这些东西像上面说的一个快捷方式就可以搞定,还去多按那么多次空格,真是浪费时间。

Eclipse里面是Ctrl+Shift+F
46 楼 moonjava 2011-02-17  
<div class="quote_title">第一条 空格,其实是 英文格式。</div>
<div class="quote_title">标点符号后面 加空格。</div>
<div class="quote_title"><br></div>
<div class="quote_title">qdpurple 写道</div>
<div class="quote_div">
<p>本文分析了jdk源代码String.class 的代码规范.</p>
<p> </p>
<p>   接触java有两年多了,真正的开发也只有不到3个月. 看平台代码才知道,自己的差距还是很远的. 各式各样的风格都有,这是与好的程序员的差距.从今天开始,我要树立自己的代码风格,模板为jdk源代码. 一些基本的规范就不列举了.</p>
<p> </p>
<p>1.空格</p>
<p>(1) 关于=号, 对变量赋值时,按照jdk源码 = 两端加两个空格,如</p>
<pre name="code" class="java">int size = original.count;</pre>
<p> (2)对于关系运算符(&gt; &lt; == ..) 两端也加空格</p>
<pre name="code" class="java">if (offset &lt; 0) {</pre>
<p>  (3) 方法后 加空格然后{</p>
<pre name="code" class="java">  public String() {</pre>
<p> (4)对于多个参数间,参数间逗号后加空格</p>
<pre name="code" class="java"> System.arraycopy(value, 0, v, 0, size);</pre>
<p> (5) if语句中 括号() 外面两侧都加一个空格</p>
<pre name="code" class="java">if (offset &lt; 0) {</pre>
<p> 2 空行</p>
<p>(1)pakage上下各一空行</p>
<p>(2)所有import中没有空行</p>
<p>(3)成员变量间有一空行</p>
<p>(4)连个方法间有一空行</p>
<p> </p>
<p>3.注释</p>
<p>(1)类注释:写出类的功能, 其它格式见下,如author, 日期格式: MM/DD/YYYY</p>
<pre name="code" class="java">/**
* The &lt;code&gt;String&lt;/code&gt; class represents character strings. All
*
* @author  Lee Boynton
* @author  Arthur van Hoff
* @version 1.189, 10/21/05
* @see     java.lang.Object#toString()
* @see     java.lang.StringBuffer
* @see     java.lang.StringBuilder
* @see     java.nio.charset.Charset
* @since   JDK1.0
*/</pre>
<p> (2)成员变量的注释:单行使用/** comment .*/ ( 注意标点)</p>
<pre name="code" class="java">  /** The value is used for character storage. */
    private final char value[];</pre>
<p> (3)方法注释: 首行写出方法作用,</p>
<p> 参数行通过@param 参数变量 参数注释.</p>
<p>异常 @exception 异常名 注释</p>
<p>返回  @return 注释</p>
<pre name="code" class="java"> /**
     * Allocates a new &lt;code&gt;String&lt;/code&gt; that contains characters
     * @param      value    array that is the source of characters.
     * @param      offset   the initial offset.
     * @param      count    the length.
     * @exception  IndexOutOfBoundsException  if the
     *        the bounds of the &lt;code&gt;value&lt;/code&gt; array.
     */
    public String(char value[], int offset, int count) {
  </pre>
<p> (5)单行注释</p>
<p>//和下面if 竖直对齐</p>
<pre name="code" class="java">        // Argument is a String
        if (cs.equals(this))</pre>
<p> </p>
</div>
<p> </p>
45 楼 djyy3273 2011-02-17  
Martin Fowler 《重构-改善既有代码的设计》每一个java程序员都应该看看的书
44 楼 gigi_ly180 2011-02-17  
的确,好的代码基本不需要注解,看方法名和参数名即知方法体的调用和执行结果
43 楼 kingsword588 2011-02-17  
还好,从开始学编程起我就一直保持这些东西,自己写出来的东西别人看不懂是很失败的...说不定过段时间自己都看不懂了..
42 楼 clarkamx 2011-02-17  
有几个规范只是英文的书写规范而已...和java无关.
41 楼 whyyczy 2011-02-17  
从重构的思想能看出来:
    必须完成功能,当前自己是啥水平,就写成啥代码。不要为了好代码分散了你完成功能的注意力;
    OK!~功能完成了,你可以重构你的代码;
    OK!~你觉得当前可以了,那就等待你觉得好像又不可以的时候继续重构;
    OK!~当你经过反反复复的重构实践,当你再次从第1步开始的时候,你的水平就不应该是最初的水平了,这就是通过重构来提升代码质量,培养编程风格。

再者,编程风格不能决定代码质量,好的编程风格可以在学习阶段就培养,但好的代码质量是通过多写代码和多思考设计模式来提升的。
40 楼 576699909 2011-02-17  
主要是自己的思路清晰, 然后是好的注释,这样最好

不然即使有注释,别人也很难明白的。
39 楼 huyong479072052 2011-02-17  
好的代码都是通过不断的重构而来的。
38 楼 Crusader 2011-02-17  
qdpurple 写道
Crusader 写道
至于命名规范,不用那么死,但最好和工作环境中其他人一致
建议可以了解下匈牙利命名法

我百度百科看了一下 匈牙利命名法 , 感觉在java中没有怎么遵守它,应该在donet中,比较流行吧


在微软系列比较流行,例如MFC
其实我比较喜欢匈牙利命名法,方法名虽然有些烦琐,但信息丰富
37 楼 waitingmyself 2011-02-17  
每个人提交代码前都要ctrl+shift+f 什么规范无所谓 关键是一个项目组要要有一个标准 最简单的当然就是ctrl+shift+f
36 楼 wesker0918 2011-02-17  
这个Ctrl+shift+F 能解决。可是同事写代码的时候类的命名他给你首字母小写,方法名首字母大写,这个真无语了。。。
35 楼 tata 2011-02-17  
可以去看看代码整洁之道的前几篇
34 楼 fly2never 2011-02-17  
Crusader 写道
至于命名规范,不用那么死,但最好和工作环境中其他人一致
建议可以了解下匈牙利命名法

java中完全不用编码缩写变量名了,匈牙利在java中没价值
33 楼 aaa5131421 2011-02-17  
编程规范要从娃娃抓起
32 楼 epgcnydy 2011-02-17  
如果开发环境用的是eclipse的话。里面可以设置代码的风格。

相关推荐

    如何培养编程能力

    通过参加开源项目、技术社区交流、阅读他人代码等方式,可以了解不同的编程风格和技巧,促进自己编程水平的提升。参加编程竞赛和编程挑战活动,也是锻炼编程能力的好方法,能够让你在有限的时间内面对复杂的问题,...

    C语言编程风格 C语言编程风格

    ### C语言编程风格详解 C语言作为一种广泛应用的编程语言,其编程风格对于代码的可读性、可维护性和效率有着至关重要的影响。良好的编程风格能够帮助开发者更好地理解代码逻辑,减少错误,提高软件质量。以下是对...

    嵌入式C语言进阶之道(C语言编程风格)

    ### 嵌入式C语言进阶之道:C语言编程风格详解 #### 一、引言 嵌入式系统开发中,C语言是最为广泛使用的编程语言之一。它以其高效、灵活的特点,成为了连接硬件与软件的重要桥梁。然而,仅仅掌握C语言的基础语法并...

    《Java编程风格》

    关于培养良好的编程风格的一些建议 希望对大家有用······

    C#编程风格Word版

    根据提供的文件信息,我们可以深入探讨其中提及的关键C#编程风格和规范知识点。下面将详细解释这些要点,并结合实际编程场景进行说明。 ### C#编程风格和规范 #### 一、重要原则 1. **保持原有风格**:在修改他人...

    AVRc语言优秀编程风格.docx

    ### AVR C语言优秀编程风格详解 #### 一、引言 在编程领域,尤其是在学习初期,掌握良好的编程风格至关重要。良好的编程风格不仅能够提高代码的可读性和可维护性,还能帮助开发者更快地成长为一名出色的程序员。...

    ACM竞赛良好的编程风格与规范介绍

    在ACM竞赛中,良好的编程风格与规范是参赛者必须掌握的基本素养,它们不仅有助于提升代码的可读性和可维护性,还能在比赛中提高解决问题的效率。以下是一些关于编程风格和规范的关键点: 1. **版权和版本声明**:在...

    Java开发规范(编程风格)

    ### Java开发规范(编程风格) #### 1. 绪论 **1.1 目的** 本规范的主要目的是为了确保组织内部能够采用统一且规范化的编程方式来编写Java代码。通过建立一套完整的编码规范,旨在培养开发人员形成良好的编码习惯...

    浅谈编程能力的培养与提高.doc

    这不仅能帮助理解不同的编程风格和最佳实践,还能启发自己的编程思路。 **3. 持续学习与技术跟进** 技术更新换代速度极快,持续学习是程序员必备的素质之一。关注最新的编程技术和工具,参与相关的在线课程和研讨...

    蓝桥杯竞赛:编程风格与代码规范的典范

    自2010年起,每年举办一次,旨在促进软件和信息技术领域专业技术人才的培养,提升高校毕业生的就业竞争力 。蓝桥杯大赛已经成为国内领先的IT学科赛事,吸引了包括北京大学、清华大学等1900余所高校参与,参赛选手总...

    浅谈《C语言程序设计》课程教学中学生编程能力的培养问题.pdf

    通过这种方式,学生可以从别人的代码中学习到好的编程习惯,也能从老师或同学的点评中发现自己的不足,从而持续改进自己的编程风格和能力。 总结 在《C语言程序设计》课程教学中,通过强调结构化编程思想、总结...

    编程高手谈编程

    如果编程风格混乱,那么不仅不利于团队协作,未来维护也是一大难题。 最后,所有专家都达成了一致的意见:创意是软件的灵魂。在这个高度竞争的软件市场中,优秀软件的标准不仅仅是功能的完备,更需要有独特的设计和...

    c/c++初级软件工程师培养计划

    在C/C++基础编程的学习中,学员将了解到C语言的历史背景、编程风格,以及它在现代计算机科学中的重要地位。指针、数组、函数等是C语言的核心概念,这些知识点的学习将帮助学员构建稳固的编程基础。同时,内存管理、...

    编程珠玑 编程珠玑续

    在《编程珠玑》中,作者Jon Bentley将编程问题比喻为“珍珠”,强调解决这些问题的过程如同寻找珍贵的珍珠,需要深思熟虑和精心打磨。书中的主要知识点包括: 1. **问题解决策略**:如何分析问题,确定合适的算法,...

    AVR&nbsp;c语言优秀编程风格

    【AVR C语言优秀编程风格】在学习编程的过程中,尤其是对于初学者来说,拥有良好的编程风格至关重要。这不仅有助于提高代码的可读性和维护性,还能帮助开发者更快地理解和解决问题。编程大师的故事告诉我们,初学者...

    编程修养-成为编程高手必备良品

    #### 培养编程修养的实践 1. **版权和版本管理**:每一份代码文件都应该包含版权和版本信息,明确记录创建者、创建日期、版本号以及修改记录。这不仅是对个人工作的尊重,也是便于后续维护和版本控制的重要措施。 ...

    编程珠玑PDF版

    - 程序员除了需要掌握基本的代码编写能力外,还需要培养良好的编程风格和习惯。良好的编程风格对于提高代码的可读性、可维护性以及团队协作至关重要。 2. 编码风格的实践: - 在编码过程中,遵守一定的编程规范,...

    代码风格代码风格的培养

    高质量的C/C++编程

    高质量C++编程

    本书写的关于C++的高质量编程,是个很不错的学习资料,可以帮助培养良好的编程习惯,有一个规范的编程风格

Global site tag (gtag.js) - Google Analytics