文档章节

零碎的知识&技能-持续更新

sweeeeeet
 sweeeeeet
发布于 2016/10/04 16:45
字数 3731
阅读 4
收藏 0
点赞 0
评论 0

一、Java的Builder模式-20161004

    在看Netty时,作者提到了Builder模式,曾经看过设计模式多次,但都没什么感觉,看过之后理解了,但很快就忘掉了,因为当时没有应用或深刻的思考过,但Builder模式是第一个让我比较“深刻”的设计模式。

    先看代码:

public class Item {
	private String name;

	private int price;

	private Date date;

	public Item(Builder builder) {
		this.name = builder.name;
		this.price = builder.price;
		this.date = builder.date;
	}

	public static class Builder {
		private String name;

		private int price = 0;

		private Date date = new Date();

		public Builder(String name) {
			this.name = name;
		}

		public Builder price(int price) {
			this.price = price;
			return this;
		}

		public Builder date(Date date) {
			this.date = date;
			return this;
		}

		public Item build() {
			return new Item(this);
		}
	}// end of Builder

	/* Item getter & setter */
}

    Builder模式应用于构造具有多参数的对象,这些参数的特点是比较灵活,可以自定义设置字段的值。这里已经很好理解了,不用再解释了。如果要构造Item对象:

Item item = new Item.Builder("name").price(10).date(new Date()).build();

    这种灵活的构造方式也可以用JavaBean的setter实现,即使用无参的构造函数,通过set方法赋值,但这种用法的缺点是在多线程并发时不能保证对象参数的一致性,不能确保线程安全。但使用Builder模式可以保证线程安全,因此Builder模式是既保证可读性,又保证安全性。

二、JavaBean的多继承-20161006

    之前遇到过一个JavaBean需要继承两个父类,两个父类分别存代表Jmx属性和Page属性。实现多继承的方式也很简单,中间加一层“代理”ProxyBean,持有两个父类对象,子类只需继承ProxyBean即可。

JmxBean:

public class JmxBean{
    private Object jxm;

    public Object getJmx(){
        return this.jxm;
    }

    public void setJmx(Object jmx){
        this.jmx = jmx;
    }
}

PageBean:

public class PageBean{
    private Object page;

    public Object getPage(){
        return this.page;
    }

    public void setPage(Object page){
        this.page = page;
    }
}

ProxyBean:

​
public class Proxybean{
    private JmxBean jmxBean;

    private PageBean pageBean;

    public Object getJmx(){
        return this.jxmBean.getJmx();
    }

    public void setJmx(Object jmx){
        this.jmxBean.setJmx(jmx);
    }

    /* pageBean同理 */
}

​

    同时又得出另一个结论,序列化框架在序列化和反序列化时调用getter、setter方法,我以前一直不理解这是为什么,而不是直接给属性。这个例子就是反例。当然现在想想这种想法还是很nc的。

三、关于对象

3.1 静态工厂方法代替构造函数-20161007

优势:

    1.静态工厂方法有名称。构造函数的参数有时并不能正确描述返回对象,所以使用带有具体名称的静态工厂方法可以避免。同时也可解决类中需要多个签名相同的构造函数。

    2.不必每次调用时创建一个新的对象。可以预先构造好实例,或将构造好的的实例缓存起来,避免创建不必要的重复对象。如果程序经常请求创建相同的对象,并且创建对象代价很高,使用静态工厂方法可以提高性能。这么做可以确保类是单例或不可实例化的。使得不可变类不存在两个相同的实例,a==b <——> a.equals(b)。

    3.可以返回原返回类型的任何子类类型对象。

    4.简化代码。

劣势:

    1.类不含有public或protected的构造函数,此类不能子类化。但这样可以更多地使用复合,即持有其他类的对象,而不是继承。

    2.与其他静态方法没有任何区别。劣势在于,静态工厂方法并没有在API中像构造函数一样标明出来,使用可能不方便。弥补的方法是使用惯用名称,例如:valueOf、getInstance、newInstance。

3.2 避免创建不必要对象-20161008

    一般来说,最好能重用对象而不是每次需要的时候创建一个功能相同的新对象,这个道理当然是很浅显的。例如String、不可变对象。当然在编码中主要对象的重用性之外,以下几点也很重要:

    1. Calendar实例代价昂贵;

    2. 延迟初始化,是不推荐的。即执行构造函数时可能创建了一些暂时不会调用到的对象、常量,将这些对象和常量在具体方法调用时再初始化。不推荐的原因是,使方法实现变得复杂,且无法显著提高性能。我觉得在工作中,尤其是新员工会经常会遇到这种问题,那么正确的方法如上所述;

    3. 自动装箱会付出一定代价,要优先使用基本类型而不是装箱基本类型;

    4.小对象创建和回收非常廉价,适当附加这种对象可提高程序可读性;

    5.只有类似数据库连接池这种大对象需要对象池,JVM垃圾回收器性能优于轻量对象池。

3.3 消除失效的对象引用-20161009

    失效的对象引用,例如实现一个类似栈的对象,这个对象自己管理内存(是出现内存泄漏的重要原因),例如执行pop,但不将被pop位置的引用置为null,则出现内存泄漏。

    1. Java中错误的操作会引起内存泄漏;

    2. 清空对象引用是一种例外,而不是规范,消除失效引用的最好方法是结束其生命周期;

    3. 如果类自己管理内存,应警惕内存泄漏问题;

    4. 使用缓存时,可以将缓存对象作为key存入WeakHashMap(真的没见过,也脑补不出应该怎么用,感觉很鸡肋),或将缓存对象存入LinkedHashMap,实现removeEldestEntry()作为淘汰策略。

四、关于类和接口

4.1 尽可能降低类的可访问性-20161023

    尽可能使每个类或者类的成员不被外界访问到,在设计类和成员时,关注这个类的可见性范围,正确使用访问级别修饰符。private及包级私有(没有修饰符)都是一个类的实现中的一部分,一般不会影响他的API。当同一个包中的另一个类真正需要访问一个成员时,才将private升级成为包级私有。

    实例中不能包含公共域(或字段、属性)。即公有类中不能包含公有域。包中含有公共可变域是线程不安全的。类中有共有静态final域是错误的,客户端可以直接修改这种域的值。如果类是包级私有或私有的嵌套类,直接暴露域是没问题的。

    其实看过Effective Java这一条才发现之前写的大部分代码都是不规范的,但没出问题的原因一是因为自己对自己的系统熟悉,不会乱写,二是缺对乏类、成员可见性的意识。

4.2 不可变类-20161023

    不可变类是实例不能被修改的。jdk中String、基本类型的包装类、BigInteger、BigDecimal是不可变的。不可变类遵循以下五条规则:

  • 不提供任何修改对象状态的方法;
  • 保证不会被扩展,一般做法是使类成为final的,另一种方法是不提供显式的构造函数,而是提供静态工厂方法构建实例,被继承必需显式的构造函数;
  • 所有域都是final的;
  • 所有域都是private的;
  • 确保对任何可变组件的互斥访问。如果类中具有可变对象的引用,确保客户端不烦获得此对象引用,也不能使用客户端提供的引用初始化改可变对象引用,也不要在任何方法中返回此对象引用。在构造器和readObject方法中使用保护性拷贝。

    不可变对象是线程安全的,不需要同步。因此可以被自由共享。除非有好的理由让类成为可变的,否则就应该是不可变的。如果类不能被做成不可变的,也应该尽可能限制他的可变性

4.3 复合优于继承-20161023

    专门设计用来继承的类,并有好的文档,在使用继承时非常安全,对于普通类进行跨包继承是非常危险的。继承打破了封装性,即超类随着版本变化会破坏子类,因此子类必须跟着超类一起变化。这也体现了专门用于继承类的优势。

    书中介绍的一个例子是扩展HashSet,并重写了add() & addAll()方法,在执行这两个方法时做累加操作,记录集合中增加的数量。错误的代码此处就不写了。这样做有非常多问题,首先HashSet的addAll方法调用了add,如果调用子类的addAll方法就做了两次计数。这种“自用性”的实现细节首先需要开发者在开发之前清楚,并且jdk不保证其他的实现也是具有自用性的,不能保证随着jdk版本的升级不发生变化,因此这个子类的实现是错误的,同时也是脆弱的。

    另一个脆弱的原因是,随着jdk版本的上升,超类可能会增加方法。而对于子类,设计子类往往是因为需要增加某些特殊的操作和限制,例如上面的例子中累加计数就是一种。如果超类增加方法,子类没有改变,那么就可以通过子类,将不符合限制的数据,没做特定操作的动作,直接通过父类提供的方法执行。并且超类新增的方法签名可能会与子类自己实现的方法签名冲突。

    复合可以解决以上问题。复合composition,即不扩展现有的类,而是在新的类中增加私有域,它引用现有类的实例,现有类成为了新类中的一个组件。新类中可以调用现有类对象的方法,并返回结果,这被称为“转发”,forwarding,新类中的方法被称为“转发方法”。在书中提供的代码中,将复合和转发分成两个类,如下:

public class InstrumentSet<E> extends ForwardingSet<E> {
	
	private int count;

	public InstrumentSet(Set<E> s) {
		super(s);
	}
	
	@Override
	public boolean add(E e) {
		count++;
		return super.add(e);
	}
	
	@Override
	public boolean addAll(Collection<? extends E> c) {
		count += c.size();
		return super.addAll(c);
	}
	
	public int count(){
		return count;
	}

}

class ForwardingSet<E> implements Set<E> {
	
	private final Set<E> s;
	
	public ForwardingSet(Set<E> s) {
		this.s = s;
	}

	@Override
	public boolean add(E e) {
		// TODO Auto-generated method stub
		return s.add(e);
	}

	@Override
	public boolean addAll(Collection<? extends E> c) {
		// TODO Auto-generated method stub
		return s.addAll(c);
	}

    /* 其他实现省略 */

}

    InstrumentSet将一个Set(构造函数传入的Set)包装成InstrumentSet,所以也称为包装类。

五、批量替换文件中字符串20161014

    今天做了一次项目文件拷贝,所有代码的package都出了问题,所以想写个脚本批量替换。命令如下:

sed -i "" "s/package com\.hippoconsoleweb/package com\.pinganfu\.hippoconsoleweb/g" `grep package -rl *`

    我的操作系统是MacOS,所以跟网上大部分的例子有点不同,mac的sed -i 需要提供一个文件后缀,做备份,如果是上面的命令,直接会覆盖所有的文件。

    sed -i:直接在文件中修改;

    grep -rl *:递归遍历所有文件并打印出来 

    如果是CentOs,去掉-i 后面的第一个参数即可

六、从protected到super.clone()-20161016

    突然发觉对Java中clone的使用很模糊,在上一个项目中用过clone方法,但现在再看发现有些不懂。于是看书、百度。

    在论坛中看到这样一句“Object的clone方法因为是protected的,所以不能直接调用”。这句话的结论是没错的,随随便便搞个对象obj.clone(),会报出method invisible的错误。当时觉得虽然结论试验出来是没错的,但他说的好像有点怪怪的。于是自己做实验,(此处脑补代码)写一个父类,子类继承,父类中有一个protected方法,当然我是写在同一个.java文件中,这也是错误的开始。这样实验的结果必定是可以访问的,因为protected方法跟调用方法的类在同一个包中。作为初学者,在看到protected时往往只考虑到了继承关系,而忘记了包关系。把父类移到另一个包中,同样报出了method invisible。最后再明确一下protected的作用域:子类或同一个包中的非子类

    再说说Cloneable接口。编码中我们通常的做法是implements Cloneable,并实现:

@Override 
public Object clone(Object o){}

    一般情况将clone方法定义为public,没有违反子类的访问修饰权限不能小于父类的 。CLoneable接口中并没有定义任何方法,他是一个标志,类似于Serializable。但如果不实现Cloneable接口,在调用Object的clone方法时,会报出CloneNotSupportedException。

    一般会在重写的clone方法中调用super.clone()。虽然调了父类的方法(其实就是Object的clone),但拷贝的并不是父类,而是子类。网上有“专家”解释了为什么会这样,算了我记不住就不转述了,反正我觉得这不是特别难理解。

    调用super.clone(),即所谓的浅拷贝。我以前一直错误的认为,浅拷贝是创建引用,而不是复制引用指向的对象。其实这种想法现在觉得很愚蠢,很容易就能举出反例。浅拷贝将原有内存,原封不动复制到另一块内存中。如果对象没有重写过equals和hashCodet方法,那么无论是==还是equals都返回false,这点其实完全可以说明浅拷贝的工作方式。引用不同,对象也不同。当然对象的不同指的是内存,不是逻辑不同。

    这样的结论可以为深拷贝、浅拷贝提供基础。既然内存一样,那么拷贝的复杂对象中如果包含对其他对象的引用,也就是所谓的拷贝的是复杂对象,就会出现引用不同但指向的对象相同这个问题。这也是深拷贝存在的原因。这一段中不会再继续说明深拷贝,因为前面的结论已经搞清楚了clone的本质。

七、Spring mvc发送和接受复杂对象数组

    直接上代码了:

前端Js:

//migrationResultList是一个复杂对象数组,对象中包含多个字段
var migrationResultList = [];
$.ajax({
		type : "POST",
		url : "submitReduce",
		contentType: "application/json; charset=utf-8",
		data : JSON.stringify(migrationResultList),
		dataType : "json",
        success : function(data){
        	
        },
	});

后端Java:

public @ResponseBody Map<String, Object> submitReduce(@RequestBody List<MigrationResult> migrationResultList) {
	return null;
}
//或者入参使用数组
public @ResponseBody Map<String, Object> submitReduce(@RequestBody MigrationResult[] migrationResultList) {
	return null;
}

 

© 著作权归作者所有

共有 人打赏支持
sweeeeeet
粉丝 2
博文 19
码字总数 24105
作品 0
上海
后端工程师
AngularJS 2 教程--ng-book 2

ng-book 2 是一个 AngularJS 2 学习教程,它能提供给你绝佳的教学指导和具有代表性的实例,让你摆脱那些混乱的指导教材,节省你的时间。如果你想在段时间内夯实基础,掌握整个框架,你应该学...

孔小菜 ⋅ 2015/07/13 ⋅ 15

摆脱职场困境:三招打造职场核心竞争力

昨天,在《得到》APP听了古典老师的直播演讲。如何打造你的职场核心竞争力。 下面是我对于古典老师演讲的理解和扩展,核心骨架未变,加入了自己的一些拓展和能够深入浅出的事例。 如何打造职...

anda0109 ⋅ 2016/12/10 ⋅ 0

《刻意学习》---持续行动是精进最大的技巧

想提高自己的学习效率,却改不掉一些坏的学习习惯和一些心理上的缺点。特别是当学习的过程当中遇到困难的时候,总是给自己找这样或者那样的借口放弃。没有持续的动力去完成想要完成的事情,做...

割草的小猪头 ⋅ 2017/11/11 ⋅ 0

【杂谈】程序员在工作期间如何对自己进行深造

前言 众所周知,我们的工作存在下面几个特点 工作时间长 工作压力大 大部分时间处于劳动密集型,累人 部门沟通繁琐,累心 由于我们每天大部分时间都是在工作,所以一直会担心,怎么不断深造。...

diandianxiyu ⋅ 2017/12/27 ⋅ 0

游戏技能系统

技能系统 A、技能的状态转换 【前摇动作 | 吟唱】--【效果延迟+(持续效果 | 瞬时效果) | 持续引导】--【后摇动作】 1、瞬发类技能:无吟唱、无效果延迟 2、吟唱类技能:需要一段时间进行施...

flyoahc ⋅ 2015/10/09 ⋅ 0

我的管理style

此文写于2014年初。 我是技术人员出身,因个人工作能力出色晋升为IT Manager。开始带人一年左右,目前目标是希望建立一个本地的小团队,加上自己就3个人而已,然后实现支持本地用户及业务持续...

simmy ⋅ 2015/09/08 ⋅ 0

如何跳出令人窒息的职场死循环

点击上方“程序人生”,选择“置顶公众号” 第一时间关注程序猿(媛)身边的故事 你的工作和生活,是这样的吗? 上班,领导派活,编码(可能是任务 X)、测试(可能是任务 Y)、解 Bug (可能...

csdnsevenn ⋅ 01/03 ⋅ 0

现实骨感只是因为理想太满

Job vs. Career vs. Calling “A calling is the most satisfying form of work because, as a gratification, it is done for its own sake rather than for the material benefits it bri......

竹说 ⋅ 2017/02/25 ⋅ 0

DeepMind新智能体架构Unicorn:持续学习能力胜过多个基准智能体

  安妮 编译自 arXiv 量子位 出品 | 公众号 QbitAI   出于未知原因,DeepMind为自己研究起的名字总饱含诗意,在学术界中显得尤为画风清奇。   比如“彩虹”,比如“独角兽”。   前者...

量子位 ⋅ 02/27 ⋅ 0

迷茫是短暂的,人生是漫长的

自从我投身于教育行业以来,也接触了很多同学,有的带着梦想和态度加入《剑指BAT》,有的带着态度加入,也有个别同学带着迷茫加入。。。作为一个过来人(其实我不想说自己是过来人,想自称“...

batbattle ⋅ 2017/12/27 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

对于程序员的招聘问题,作为软件人的一些吐槽和建议

作为软件人,找工作有时候似乎挺苦逼的。 说真的,让我去掉前面这句中“似乎”二字吧。就是苦逼!很多人都曾抱怨处在招聘的一方很糟糕——我们没有任何可靠的方式来甄别会写代码并且写得好的...

老道士 ⋅ 15分钟前 ⋅ 0

HDFS原理学习

一、概述 1、 Hadoop整合了众多的文件系统,首先提供了一个高层的文件系统抽象org.apache.hadoop.fs.FileSystem。然后有各个文件系统的实现类。 2、Hadoop是JAVA编写的,不同文件系统之间的交...

cjxcloud ⋅ 19分钟前 ⋅ 0

Linux下MySQL表名不区分大小写的设置方法(抄袭别人的)

Linux下MySQL表名不区分大小写的设置方法 MySQL表名不区分大小写的设置方法 在用centox安装mysql后,把项目的数据库移植了过去,发现一些表的数据查不到,排查了一下问题,最后发现是表名的大...

随风而浮沉 ⋅ 24分钟前 ⋅ 0

ubuntu下安装宋体simsun

sudo cp simsun.ttc /usr/share/fonts cd /usr/share/fonts sudo chmod 644 simsun.ttc 更新字体缓存: 代码: sudo mkfontscale 代码: sudo mkfontdir 代码: sudo fc-cache -fsv 安装chrome扩......

wangxuwei ⋅ 25分钟前 ⋅ 0

利用 ssh 传输文件

Linux 下一般可以用 scp 命令通过 ssh 传送文件: #把服务器上的 /home/user/a.txt 发送到本机的 /var/www/local_dir 目录下scp username@servername:/home/user/a.txt /var/www/local_dir...

大灰狼时间 ⋅ 36分钟前 ⋅ 0

web3j教程:android和java程序员如何使用web3j开发区块链以太坊

如何使用web3j为Java应用或Android App增加以太坊区块链支持,本教程内容即涉及以太坊中的核心概念,例如账户管理包括账户的创建、钱包创建、交易转账,交易与状态、智能合约开发与交互、过滤...

智能合约 ⋅ 58分钟前 ⋅ 0

web3j开发java或android以太坊智能合约快速入门

web3j简介 web3j是一个轻量级、高度模块化、响应式、类型安全的Java和Android类库提供丰富API,用于处理以太坊智能合约及与以太坊网络上的客户端(节点)进行集成。 可以通过它进行以太坊区块链...

笔阁 ⋅ 今天 ⋅ 0

一起读书《深入浅出nodejs》-异步I/O

异步I/O “异步”这个名词其实很早就诞生了,但它大规模流行却是在Web 2.0浪潮中,它伴随着AJAX的第一个A(Asynchronous)席卷了Web。 为什么要异步I/O 关于异步I/O为何在Node里如此重要,这与...

小草先森 ⋅ 今天 ⋅ 0

JVM各种问题

1、如果启动什么都不设,会怎样? 先来看一个命令 [root@localhost bin]# java -XX:+PrintCommandLineFlags -version -XX:InitialHeapSize=29899008 -XX:MaxHeapSize=478384128 -XX:+PrintCo......

算法之名 ⋅ 今天 ⋅ 0

SAS笔记-宏2

宏是一种文本,一般来说其编译是在程序执行之前。 宏变量的创建 %let语句 %let macro_variables = text; %let是常见的宏变量建立方式,其编译就在执行前。如下例中,想要宏变量test等于数据集...

tonorth123 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部