目录
一、java基础
1、==与equls
在java中,==主要有两个作用:
1)基础数据类型:比较的是他们的值是否相等,比如两个int类型的变量,比较的是变量的值是否一样。
2)引用数据类型:比较的是引用的地址是否相同,比如说新建了两个User对象,比较的是两个User的地址是否一样。
OK。到这就注意了,你会发现,我在举引用的例子的时候,使用的是User对象,而不是String。别着急接着往下看。
在java中,equals主要有两个作用:
1)没重写时,equals方法,比如User等自定义类,都是比较内存地址。
2)重写了时,如String的引用的对象,内容是否相等
2、Enum类型
1)介绍:自己定义的常量类型与Enum枚举类型相比存在着类型不安全、没有命名空间、一致性差、类型无意义等问题,因此引入了Enum类型。
2)定义Enum类型
//定义一周七天的枚举类型
public enum WeekDayEnum {Mon,Tue,Wed,Thu,Fri,Sat,Sun}
//读取当天的信息
WeekDayEnum today = readToday();
//根据日期来选择进行活动
switch(today){
Mon:do something;break;
Tue:do something;break;
Wed:do something;break;
Thu:do something;break;
Fri:do something;break;
Sat:do something;break;
Sun:do something;break;
}
对于这些枚举的日期,JVM都会在运行期间构造出一个简单的对象实例一一对应。这些对象都有唯一的identity,类似整型数值一样,switch语句就根据此来进行执行跳转。
3)定制枚举类型。通过给枚举类型增加一些复杂功能,例如给enum类型增加属性。
// 定义 RSS(Really Simple Syndication) 种子的枚举类型
public enum NewsRSSFeedEnum {
// 雅虎头条新闻 RSS 种子
YAHOO_TOP_STORIES("<a href="http://rss.news.yahoo.com/rss/topstories"><code>http://rss.news.yahoo.com/rss/topstories</code></a>"),
//CBS 头条新闻 RSS 种子
CBS_TOP_STORIES("<a href="http://feeds.cbsnews.com/CBSNewsMain?format=xml"><code>http://feeds.cbsnews.com/CBSNewsMain?format=xml</code></a>"),
// 洛杉矶时报头条新闻 RSS 种子
LATIMES_TOP_STORIES("<a href="http://feeds.latimes.com/latimes/news?format=xml"><code>http://feeds.latimes.com/latimes/news?format=xml</code></a>");
// 枚举对象的 RSS 地址的属性
private String rss_url;
// 枚举对象构造函数
private NewsRSSFeedEnum(String rss) {
this.rss_url = rss;
}
// 枚举对象获取 RSS 地址的方法
public String getRssURL() {
return this.rss_url;
}
}
4)易错注意。
(1)enum类型不支持public和protected修饰符的构造方法,因此构造函数一定要是private或friendly的。也正因为如此,所以枚举对象是无法在程序中通过直接调用其构造方法来初始化的。
(2)定义enum类型时候,如果是简单类型,那么最后一个枚举值后不用跟任何一个符号;但如果有定制方法,那么最后一个枚举值与后面代码要用分号‘;’隔开,不能用逗号或空格。
(3)由于enum类型的值实际上是通过运行期构造出对象来表示的,所以在cluster环境下,每个虚拟机都会构造出一个同义的枚举对象。因而在作比较操作时候就需要注意,如果直接通过使用符号(‘==’)操作符,这些看似一样的枚举值一定不相等,因为这不是同一个对象实例。
优秀的回答链接:IBM Developer 正在整合其语言站点组合。 – IBM Developer
3、转型的意义
向上转型不多说,此处主要结束向下转型的意义;
向下转型的意义:简而言之就是,在向下转型的后,可以找回(使用)子类的方法。
优秀的回答链接:Java向下转型的意义_yinhuanxu-优快云博客_java向下转型
4、全局变量的使用
5、序列化与反序列化
byte在序列化的时候,会自动base64加密,所以在反序列化时需要解一下。
优秀文章链接:序列化和反序列化 - 简书
6、静态方法与非静态方法的区别
静态方法和实例方法的区别主要体现在两个方面:
第一,在外部调用静态方法时,可以使用“类名.方法名”的方式,也可以使用“对象名.方法名”的方式。而实例方法只有后面这种方式,也就是说,调用静态方法可以无需创建对象。
第二,静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法,实例方法则无此限制。
概括:静态方法只能访问静态成员,实例方法可以访问静态和实例成员 。
7、String.format("%s-win",System.getProperty("os.arch")).toString();
1)System.getProperty("")获取当前操作系统的信息。
os.name 当前操作系统的名称 os.arch 当前操作系统的架构(即32位还是64位,一半是看你的jre版本)
os.version 当前操作系统版本 file.separator File separator ("/" on UNIX)
path.separator Path separator (":" on UNIX) line.separator Line separator ("\n" on UNIX)
user.name 用户账户名 user.home 用户home目录
user.dir 用户当前工作目录
2)Java字符串格式化-String.format()的使用
优秀链接:JAVA字符串格式化-String.format()的使用_lonely_fireworks的专栏-优快云博客_string.format
8、void和return的关系
Void和return 的关系:在返回类型为 void 的函数中,return 返回语句不是必需的,隐式的 return 发生在函数的最后一个语句完成时。一般情况下,返回类型是 void 的函数使用 return 语句是为了引起函数的强制结束。
二、功能特性
1、String...
String...是java5新加入的功能,表示的是一个可变长度的参数列表。其语法就是类型后跟...,表示此处接受的参数为0到多个Object类型的对象,或者是一个Object[]。例如,我们有一个方法叫做test(String... strings),那么你还可以写方法test(),但你不能写test(String[] strings),这样会出现重复方法错误。
在使用的时候,对于test(String...strings),你可以直接用test()去调用,表示没有参数,也可以用test("aaa"),也可以用test(new String[]{"aaa","bbb"})
另外如果既有test(String...strings)函数,又有test()函数,我们在调用test()时,会优先使用test()函数。
2、23种设计模式
2.1、工厂模式
3、ProcessBuilder
4、Spring的IOC映射原理
Spring的IOC原理(通俗易懂)_love_everybody的博客-优快云博客_ioc
spring ioc原理(看完后大家可以自己写一个spring)_it_man的专栏-优快云博客_springioc原理
三、基本代码开发规范
1、常量定义
1)不允许任何魔法值(即未经预先定义的常量)直接出现在代码中。
反例:String key = "Id#gscloud_" + tradeId;
cache.put(key, value);
2)在 long 或者 Long 赋值时,数值后使用大写的 L,不能是小写的 l,小写容易跟数字
1 混淆,造成误解。
说明:Long a = 2l; 写的是数字的 21,还是 Long 型的 2?
3)不要使用一个常量类维护所有常量,要按常量功能进行归类,分开维护。
说明:大而全的常量类,杂乱无章,使用查找功能才能定位到修改的常量,不利于理解和维护。
正例:缓存相关常量放在类 CacheConsts 下;系统配置相关常量放在类 ConfigConsts 下。
4)常量的复用层次有五层:跨应用共享常量、应用内共享常量、子工程内共享常量、包 内共享常量、类内
共享常量。
(1) 跨应用共享常量:放置在二方库中,通常是 client.jar 中的 constant 目录下。
(2) 应用内共享常量:放置在一方库中,通常是子模块中的 constant 目录下。
反例:易懂变量也要统一定义成应用内共享常量,两位攻城师在两个类中分别定义了表示
“是”的变量:
类 A 中:public static final String YES = "yes";
类 B 中:public static final String YES = "y"; A.YES.equals(B.YES),预期是 true,但实际返回
为 false,导致线上问题。
(3) 子工程内部共享常量:即在当前子工程的 constant 目录下。
(4) 包内共享常量:即在当前包下单独的 constant 目录下。
(5) 类内共享常量:直接在类内部 private static final 定义。
5)如果变量值仅在一个固定范围内变化用 enum 类型来定义。说明:如果存在名称之外的延伸属性应使用
enum 类型,下面正例中的数字就是延伸信息,表示一年中的第几个季节。
正例:
public enum SeasonEnum {
SPRING(1), SUMMER(2), AUTUMN(3), WINTER(4);
private int seq;
SeasonEnum(int seq){
this.seq = seq;
}
}
2、代码格式
1)大括号的使用约定。如果是大括号内为空,则简洁地写成{}即可,不需要换行;如果是非空代码块则:
(1) 左大括号前不换行。
(2) 左大括号后换行。
(3) 右大括号前换行。
(4) 右大括号后还有 else 等代码则不换行;表示终止的右大括号后必须换行。
2)左小括号和字符之间不出现空格;同样,右小括号和字符之间也不出现空格;而左大括号前需要空格。
详见第 5 条下方正例提示
反例:if (空格 a == b 空格)
3)if/for/while/switch/do 等保留字与括号之间都必须加空格。
4)任何二目、三目运算符的左右两边都需要加一个空格。说明:运算符包括赋值运算符=、逻辑运算符&&、
加减乘除符号等。
5)采用 4 个空格缩进,禁止使用 tab 字符。
说明:如果使用 tab 缩进,必须设置 1 个 tab 为 4 个空格。IDEA 设置 tab 为 4 个空格时,请勿勾选 Use tab character; 而在 eclipse 中,必须勾选 insert spaces for tabs。
正例:(涉及 1-5 点)
public static void main(String[] args) {
// 缩进 4 个空格
String say = "hello";
// 运算符的左右必须有一个空格
int flag = 0;
// 关键词 if 与括号之间必须有一个空格,括号内的 f 与左括号,0 与右括号不需要空格
if (flag == 0) {
System.out.println(say);
}
// 左大括号前加空格且不换行;左大括号后换行
if (flag == 1) {
System.out.println("world");
// 右大括号前换行,右大括号后有 else,不用换行
} else {
System.out.println("ok");
// 在右大括号后直接结束,则必须换行
}
}
6)注释的双斜线与注释内容之间有且仅有一个空格。
正例:
// 这是示例注释,请注意在双斜线之后有一个空格
String ygb = new String();
7)单行字符数限制不超过 120 个,超出需要换行,换行时遵循如下原则:
(1) 第二行相对第一行缩进 4 个空格,从第三行开始,不再继续缩进,参考示例。
(2) 运算符与下文一起换行。
(3) 方法调用的点符号与下文一起换行。
(4) 方法调用中的多个参数需要换行时,在逗号后进行。
(5) 在括号前不要换行,见反例。
正例:
StringBuffer sb = new StringBuffer();
// 超过 120 个字符的情况下,换行缩进 4 个空格,点号和方法名称一起换行
sb.append("zi").append("xin")...
.append("huang")...
.append("huang")...
.append("huang");
反例:
StringBuffer sb = new StringBuffer();
// 超过 120 个字符的情况下,不要在括号前换行
sb.append("zi").append("xin")...append ("huang");
// 参数很多的方法调用可能超过 120 个字符,不要在逗号前换行
method(args1, args2, args3, ...
, argsX);
8)方法参数在定义和传入时,多个参数逗号后边必须加空格。
正例:下例中实参的 args1,后边必须要有一个空格。
method(args1, args2, args3);
9)IDE 的 text file encoding 设置为 UTF-8; IDE 中文件的换行符使用 Unix 格式,不要使用 Windows 格式。
10)单个方法的总行数不超过 80 行。
说明:包括方法签名、结束右大括号、方法内代码、注释、空行、回车及任何不可见字符的总行数不超过 80 行。
正例:代码逻辑分清红花和绿叶,个性和共性,绿叶逻辑单独出来成为额外方法,使主干代码 更加清晰;共性逻辑抽取 成为共性方法,便于复用和维护。
11)没有必要增加若干空格来使某一行的字符与上一行对应位置的字符对齐。
正例:
int one = 1; long two = 2L;
float three = 3F;
StringBuffer sb = new StringBuffer();
说明:增加 sb 这个变量,如果需要对齐,则给 a、b、c 都要增加几个空格,在变量比较多的情况下,是非 常累赘的事情。
12)不同逻辑、不同语义、不同业务的代码之间插入一个空行分隔开来以提升可读性。
说明:任何情形,没有必要插入多个空行进行隔开
3、OOP规约
1)避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可。
2)所有的覆写方法,必须加@Override 注解。
说明: getObject()与 get0bject()的问题。一个是字母的 O,一个是数字的 0,加@Override 可以准确判断是 否覆盖成功。 另外,如果在抽象类中对方法签名进行修改,其实现类会马上编译报错。
3) 相同参数类型,相同业务含义,才可以使用 Java 的可变参数,避免使用 Object。
说明:可变参数必须放置在参数列表的最后。
正例:public List<User> listUsers(String type, Long... ids) {...}
4)外部正在调用或者二方库依赖的接口,不允许修改方法签名,避免对接口调用方产生 影响。接口过时必
须加@Deprecated 注解,并清晰地说明采用的新接口或者新服务是什么。
5) 不能使用过时的类或方法。
说明:java.net.URLDecoder 中的方法 decode(String encodeStr) 这个方法已经过时,应该使用双参数
decode(String source, String encode)。接口提供方既然明确是过时接口,那么有义务同时提供新的接
口;作为调用方来说,有义务去考证过时方法的新实现是什么。
6)Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用 equals。
正例:"test".equals(object);
反例:object.equals("test");
说明:推荐使用 java.util.Objects#equals(JDK7 引入的工具类)
7)所有的相同类型的包装类对象之间值的比较,全部使用 equals 方法比较。 说明:对于 Integer var
= ? 在-128 至 127 范围内的赋值,Integer 对象是在 IntegerCache.cache 产生,会复用已有对象,
这个区间内的 Integer 值可以直接使用==进行判断,但是这个区间之外的所有数据,都会在堆上产生,
并不会复用已有对象,这是一个大坑,推荐使用 equals 方法进行判断。
8)关于基本数据类型与包装数据类型的使用标准如下:
(1) 所有的 POJO 类属性必须使用包装数据类型。
(2) RPC 方法的返回值和参数必须使用包装数据类型
(3) 所有的局部变量使用基本数据类型。
说明:POJO 类属性没有初值是提醒使用者在需要使用时,必须自己显式地进行赋值,任何 NPE 问题,或
者入库检查,都由使用者来保证。
正例:数据库的查询结果可能是 null,因为自动拆箱,用基本数据类型接收有 NPE 风险。反例:比如显
示成交总额涨跌情况,即正负 x%,x 为基本数据类型,调用的 RPC 服务,调用不成功时,返回的是默认
值,页面显示为 0%,这是不合理的,应该显示成中划线。所以包装数据类型的 null 值,能够表示额外
的信息,如:远程调用失败,异常退出。
9)定义 DO/DTO/VO 等 POJO 类时,不要设定任何属性默认值。
反例:POJO 类的 gmtCreate 默认值为 new Date(),但是这个属性在数据提取时并没有置入具体值,在更新其 它字段时又 附带更新了此字段,导致创建时间被修改成当前时间。
10) 序列化类新增属性时,请不要修改 serialVersionUID 字段,避免反序列失败;如果完全不兼容升级,避免
反序列化混乱,那么请修改 serialVersionUID 值。
说明:注意 serialVersionUID 不一致会抛出序列化运行时异常。
11) 构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在 init 方法中。
12 ) POJO 类必须写 toString 方法。使用 IDE 中的工具:source> generate toString 时,如果继承了另一个 POJO 类,注意在 前面加一下 super.toString。
说明:在方法执行抛出异常时,可以直接调用 POJO 的 toString()方法打印其属性值,便于排查问题。
13)禁止在 POJO 类中,同时存在对应属性 xxx 的 isXxx()和 getXxx()方法。
说明:框架在调用属性 xxx 的提取方法时,并不能确定哪个方法一定是被优先调用到。
14) 使用索引访问用 String 的 split 方法得到的数组时,需做最后一个分隔符后有无内容的检查,否则会有抛
IndexOutOfBoundsException 的风险。
说明:
String str = "a,b,c,,";
String[] ary = str.split(",");
// 预期大于 3,结果是 3 System.out.println(ary.length);
15) 当一个类有多个构造方法,或者多个同名方法,这些方法应该按顺序放置在一起,便于阅读,此条规则优
先于第 16 条规则。
16)类内方法定义的顺序依次是:公有方法或保护方法 > 私有方法 > getter/setter 方法。
说明:公有方法是类的调用者和维护者最关心的方法,首屏展示最好;保护方法虽然只是子类关心,也可能
是“模板设计模式”下的核心方法;而私有方法外部一般不需要特别关心,是一个黑盒实现;因为承载的
信息价值较低,所有 Service 和 DAO 的 getter/setter 方法放在类体最后。
17) setter 方法中,参数名称与类成员变量名称一致,this.成员名 = 参数名。
在 getter/setter 方法中,不要增加业务逻辑,增加排查问题的难度。
18)循环体内,字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展。
说明:下例中,反编译出的字节码文件显示每次循环都会 new 出一个 StringBuilder 对象,然后进行 append
操作,最后通过 toString 方法返回 String 对象,造成内存资源浪费。
反例:
String str = "start";
for (int i = 0; i < 100; i++) {
str = str + "hello";
}
19)final 可以声明类、成员变量、方法、以及本地变量,下列情况使用 final 关键字:
(1) 不允许被继承的类,如:String 类。 (2) 不允许修改引用的域对象。 (3) 不允许被重写的方法,如:POJO 类的 setter 方法。 (4) 不允许运行过程中重新赋值的局部变量。 (5) 避免上下文重复使用一个变量,使用 final 描述可以强制重新定义一个变量,方便更好地进行重 构。
20) 慎用 Object 的 clone 方法来拷贝对象。
说明:对象的 clone 方法默认是浅拷贝,若想实现深拷贝需要重写 clone 方法实现域对象的深度遍历式拷 贝。
21. 类成员与方法访问控制从严:
(1) 如果不允许外部直接通过 new 来创建对象,那么构造方法必须是 private。 (2) 工具类不允许有 public 或 default 构造方法。 (3) 类非 static 成员变量并且与子类共享,必须是 protected。 (4) 类非 static 成员变量并且仅在本类使用,必须是 private。 (5) 类 static 成员变量如果仅在本类使用,必须是 private。 (6) 若是 static 成员变量,考虑是否为 final。 (7) 类成员方法只供类内部调用,必须是 private。 (8) 类成员方法只对继承类公开,那么限制为 protected。
说明:任何类、方法、参数、变量,严控访问范围。过于宽泛的访问范围,不利于模块解耦。
思考:如果是一个 private 的方法,想删除就删除,可是一个 public 的 service 成员方法或 成员变量,删 除一下,不得手心冒点汗吗?变量像自己的小孩,尽量在自己的视线内,变量作用域太大,无限制的到处跑, 那么你会担心的。
4、集合处理
1) 关于 hashCode 和 equals 的处理,遵循如下规则:
(1) 只要重写 equals,就必须重写 hashCode。 (2) 因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断, 所以 Set 存储的 对象必须重写这两 个方法。 (3) 如果自定义对象作为 Map 的键,那么必须重写 hashCode 和 equals。
说明:String 重写了 hashCode 和 equals 方法,所以我们可以非常愉快地使用 String 对象作为 key 来使用。
2) ArrayList 的 subList 结果不可强转成 ArrayList,否则会抛出 ClassCastException 异常,即
java.util.RandomAccessSubList cannot be cast to java.util.ArrayList。
说明:subList 返回的是 ArrayList 的内部类 SubList,并不是 ArrayList 而是 ArrayList 的一个视
图,对于 SubList 子列表的所有操作最终会反映到原列表上。
3)在 subList 场景中,高度注意对原集合元素的增加或删除,均会导致子列表的遍历、增加、删除产 生 ConcurrentModificationException 异常。
四、个人总结
- Java中的User.dir: System.getProperty("user.dir")获取当前项目的根目录。
- Path.resolve:该方法用于将给定的路径字符串转换为Path,并以与resolve方法指定的完全相同的方式针对此Path对其进行解析。
- JSON解析:Java 中 JSON 的使用 | 菜鸟教程,获取两个#@#中间的值,直接split,取中间值。
- 如何建立通用的静态方法,去调用非静态的mapper。
方法:使用SpringContextHolder.getBean();//查省份 public static List<Xzqh> getProvince() { List<Xzqh> province = new ArrayList<>(); try{ PriceEvalOrgMapper priceEvalOrgMapper = SpringContextHolder.getBean(PriceEvalOrgMapper.class); province = priceEvalOrgMapper.getProvince(); }catch (Exception e){ e.printStackTrace(); } return province; }
五、错题
1、构造函数
构造函数是类的一种特殊函数,它的方法名必须与类名相同
构造函数的主要作用是完成对类的对象的初始化工作
一般在创建新对象时,系统会自动调用构造函数
易错点:每个类有且只能有一个构造函数。(可以有无数个)
2、+号
如果int x=20, y=5,则语句System.out.println(x+y +""+(x+y)+y); 的输出结果是()
解释:
1)不论有什么运算,小括号的优先级都是最高的,先计算小括号中的运算,得到x+y +""+25+y
2)任何字符与字符串相加都是字符串,但是是有顺序的,字符串前面的按原来的格式相加,字符串后面的都按字符串相加,得 到25+“”+25+5
3)上面的结果按字符串相加得到25255
3、split错题
String str = "";
System.out.print(str.split(",").length);
注意2点: 1:String获取长度用的是length()方法,而数组类型我们直接用属性length获取长度,所以String[]数组类型我们应该用length获取长度;2:总结来说,因为原字符串不包含分隔符,所以直接返回原字符串,分割出来只有一个空的字符串数组,所以结果是1.(注意,虽然原字符串为空,存到字符串数组为空,但是这个空也会算一个元素。)
3、String类型的不可变
String类中使用字符数组保存字符串,如:private final char value[];因为有“final”修饰符,所以可以知道string对象是不可变的,String为不可变对象,一旦被创建,就不能修改它的值,但是如下例子所示:
String s="ABC";
s=s+"abc";
System.out.println(s);
最后输出的结果是ABCabc,不是应该不可变吗?看似自相矛盾,其实String对象不可改变,但是可以改变定义的String类型的变量指向,即一旦进行了S=S+“abc”的操作,那么就创建了一个新的String来存放新的内容,原先的对象依旧存在在内存中,在等待后来java虚拟机回收。