<![CDATA[viLuo的世界]]> http://viluo.com/ zh-cn www.emlog.net JAVA JDK6内置支持的webservice使用示例 http://viluo.com/post/60 webService是一种跨语言的系统间交互标准。在java中使用webservice根据服务器端的服务根据描述生成WSDL文件,并将应用与此WSDL文件一起放入HTTP服务器中,借助服务工具根据WSDL文件生成客户端STUB代码。此代码的作用是将产生的对象请求信息封装成标准的SOAP格式数据,并发送到服务器端,服务器端根据接收到的SOAP格式数据进行转换,并最终通过反射调用响应类的响应方法。

jdk 6中集成了WEB service的支持,通过WebService 的annotation来暴露服务的实现,并通过Endpoint.publish将服务发布到指定的地址,客户端通过wsimport来访问响应地址的wsdl文件,生成调用服务器端服务的stub类信息,客户端即可通过生成的类来调用服务器的服务了。

具体示例如下:

1.对外暴露的接口

public interface TestWebService {
public String echo();
}

2.服务器端的实现类,并通过@WebService来指定对外提供服务的服务名称,客户端生成的类目和包名

import javax.jws.WebService;
import javax.xml.ws.Endpoint;
@WebService(name="MyTestWS",serviceName="MyWebService",targetNamespace="http://localhost/client")
public class WebServiceImpl implements TestWebService{
@Override
public String echo() {
return "webservice return msg";
}
public static void main(String[] args) {
Endpoint.publish("http://localhost:8080/MyWebService", new WebServiceImpl());
}
}

附javax.jws.WebService类中方案的说明

package javax.jws;
import java.lang.annotation.Annotation;
public interface WebService extends Annotation {
/**
* Web Service 的名称。
* 当映射到 WSDL 1.1 时,该名称被用作 wsdl:portType 的名称。
* specdefault Java 类或接口的简单名称。
* */
public abstract String name();
/**
* 如果 @WebService.targetNamespace 注释是关于某一服务端点接口的,
* 则 targetNamespace 用于 wsdl:portType(以及关联的 XML 元素)的名称空间。
* 如果 @WebService.targetNamespace 注释是关于某个没有引用服务端点接口(通过 endpointInterface 属性)的服务实现 bean 的,
* 则 targetNamespace 既可以用于 wsdl:portType 也可以用于 wsdl:service(以及关联的 XML 元素)。
* 如果 @WebService.targetNamespace 注释是关于某个引用服务端点接口(通过 endpointInterface属性)的服务实现 bean 的,
* 则 targetNamespace 仅用于 wsdl:service(以及关联的 XML 元素)。
* specdefault 随实现而定,正如 JAX-WS 2.0 [5] 第 3.2 节中描述的那样。
* */
public abstract String targetNamespace();
/**
* Web Service 的服务名称。
* 当映射到 WSDL 1.1 时,此名称被用作 wsdl:service 的名称。
* 不允许在端点接口上使用此成员值。
* specdefault Java class + "Service" 的简单名称。
* */
public abstract String serviceName();
/**
* Web Service 的端口名称。
* 当映射到 WSDL 1.1 时,此名称被用作 wsdl:port 的名称。
* 不允许在端点接口上使用此成员值。
* specdefault @WebService.name+"Port"。
* */
public abstract String portName();
/**
* 描述服务的预定义 WSDL 的位置。
* wsdlLocation 是引用预先存在的 WSDL 文件的 URL(相对或绝对)。
* wsdlLocation值的存在指示服务实现 bean 将实现预先定义的 WSDL 协定。
* 如果服务实现 bean 与此 WSDL 中声明的 portType和绑定不一致,则 JSR-181 工具必须提供反馈。
* 注意,单个 WSDL 文件可能包含多个 portType 和多个绑定。
* 服务实现 bean上的注释确定对应于 Web Service 的特定 portType 和绑定。
* */
public abstract String wsdlLocation();
/**
* 定义服务抽象 Web Service 协定的服务端点接口的完整名称。
* 此注释允许开发人员将接口协定与实现分离。
* 如果此注释存在,则使用服务端点接口来确定抽象 WSDL 协定(portType和绑定)。
* 服务端点接口可以包含用来定制 Java 到 WSDL的映射关系的 JSR-181 注释。
* 服务实现 bean 可以实现服务端点接口,但并不要求这样做。
* 如果此成员值不存在,则 Web Service协定是根据服务实现 bean 上的注释生成的。
* 如果目标环境需要一个服务端点接口,则将生成一个由实现定义的包,该包具有由实现定义的名称。
* 不允许在端点接口上使用此成员值。
* */
public abstract String endpointInterface();
}

3.然后运行服务器的WebServiceImpl的main函数,暴露服务并将服务注册到一个http服务地址上,客户端通过jdk的bin下面的wsimport命令来获取服务器的wsdl文件并生成客户端的stub类信息

wsimport -keep http://localhost:8080/MyWebService?wsdl  

4.然后在你的路径上就会生成下面几个类

点击查看原图

5.然后我们编写客户端的调用代码

import localhost.client.MyWebService;
public class WebServiceClient {
public static void main(String[] args) {
MyWebService myWebService = new MyWebService();
System.out.println(myWebService.getMyTestWSPort().echo());
}
}

6.执行客户端的调用代码,输出如下:

点击查看原图

7.我们看下最终生成客户端服务调用的类内容

package localhost.client;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import javax.xml.ws.WebEndpoint;
import javax.xml.ws.WebServiceClient;
/**
* This class was generated by the JAXWS SI.
* JAX-WS RI 2.0_02-b08-fcs
* Generated source version: 2.0
*
*/
@WebServiceClient(name = "MyWebService", targetNamespace = "http://localhost/client", wsdlLocation = "http://localhost:8080/MyWebService?wsdl")
public class MyWebService extends Service{
private final static URL MYWEBSERVICE_WSDL_LOCATION;
static {
URL url = null;
try {
url = new URL("http://localhost:8080/MyWebService?wsdl");
} catch (MalformedURLException e) {
e.printStackTrace();
}
MYWEBSERVICE_WSDL_LOCATION = url;
}
public MyWebService(URL wsdlLocation, QName serviceName) {
super(wsdlLocation, serviceName);
}
public MyWebService() {
super(MYWEBSERVICE_WSDL_LOCATION, new QName("http://localhost/client", "MyWebService"));
}
/**
*
* @return
*     returns MyTestWS
*/
@WebEndpoint(name = "MyTestWSPort")
public MyTestWS getMyTestWSPort() {
return (MyTestWS)super.getPort(new QName("http://localhost/client", "MyTestWSPort"), MyTestWS.class);
}
}
]]>
Fri, 24 May 2013 03:20:53 +0000 viLuo http://viluo.com/post/60
阿里云服务器推荐有礼 http://viluo.com/post/59 最近购买了一款阿里云服务器,最低的配置,ubuntu的系统,嘿嘿,现在还在上手熟悉中!

一直想买阿里云的服务器,它的优势就是扩展性比较强,cpu、内存、磁盘、带宽,都可以按需配置,而且价格相比VPS那些也便宜了不少。在购买完之后,和可以动态扩充自己的配置。不过最近发现一个不太好的就是,它的磁盘空间只能扩展3次,可能自己还没研究透吧。

铺了不少废话,现在进入正题了,最近阿里云服务器在做一个促销,如果你在阿里云下没有服务器,那么通过下面这个链接购买大于99云的产品,你就能得到5%的优惠,虽然不是很多,但是能优惠一点是一点,对吧:-D。

优惠地址:

]]>
Mon, 25 Feb 2013 03:40:47 +0000 viLuo http://viluo.com/post/59
一个简单的使用Maven创建的web项目 http://viluo.com/post/58 前言

最近在新项目中使用了Maven,由于更多的工作量放在业务上面,所以对maven使用的不是很深刻。今天借着项目快要完成之际,来自己尝试使用Maven创建一个简单的的项目。如果你已经步入了Maven的殿堂,那么,请忽略本文吧:-D。本文纯粹做个备忘录:-D

准备

首先可以看下这两篇基础的文章:《使用Eclipse构建Maven项目》、《一个Servlet的简单示例Hello World!》。

步骤

  • 1.首先使用Maven创建web项目(可参考《使用Eclipse构建Maven项目》),目录节点如下图
  • 点击查看原图

  • 2.创建HelloWorld文件(可参考《一个Servlet的简单示例Hello World!》)
  • 3.配置web.xml文件(可参考《一个Servlet的简单示例Hello World!》)
  • 4.配置pom.xml文件,需要关注dependencies和plugins节点
  • <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>testweb</groupId>
    <artifactId>com.viluo.testweb</artifactId>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>com.viluo.testweb Maven Webapp</name>
    <url>http://maven.apache.org</url>
    <dependencies>
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>3.8.1</version>
    <scope>test</scope>
    </dependency>
    <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.4</version>
    </dependency>
    <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet.jsp</artifactId>
    <version>2.0.0.v200706191603</version>
    </dependency>
    </dependencies>
    <properties>
    <jetty.version>6.1.26</jetty.version>
    </properties>
    <build>
    <finalName>com.viluo.testweb</finalName>
    <plugins>
    <plugin>
    <groupId>org.mortbay.jetty</groupId>
    <artifactId>maven-jetty-plugin</artifactId>
    <version>${jetty.version}</version>
    <configuration>
    <webAppConfig>
    <contextPath>/testweb</contextPath>
    </webAppConfig>
    </configuration>
    </plugin>
    </plugins>
    </build>
    </project>
  • 5.在Debug Configurations配置testweb-build和testweb-run
  • 点击查看原图 点击查看原图
  • 6.运行testweb-build构建项目,再运行testweb-run运行项目
  • 7.通过http://localhost:8080/testweb/hw访问,OK
]]>
Fri, 28 Dec 2012 12:25:29 +0000 viLuo http://viluo.com/post/58
Servlet的相关的知识 http://viluo.compost/57 一,什么是Servlet?

Servlet是一个Java编写的程序,此程序是基于Http协议的,在服务器端运行的(如tomcat),是按照Servlet规范编写的一个Java类。

二,Servlet有什么作用?

主要是处理客户端的请求并将其结果发送到客户端。

三,Servlet的生命周期?

Servlet的生命周期是由Servlet的容器来控制的,它可以分为3个阶段;初始化,运行,销毁。

初始化阶段:

  • 1.Servlet容器加载servlet类,把servlet类的.class文件中的数据读到内存中。
  • 2.然后Servlet容器创建一个ServletConfig对象。ServletConfig对象包含了Servlet的初始化配置信息。
  • 3.Servlet容器创建一个servlet对象。
  • 4.Servlet容器调用servlet对象的init方法进行初始化。

运行阶段:

当servlet容器接收到一个请求时,servlet容器会针对这个请求创建servletRequest和servletResponse对象。然后调用service方法。并把这两个参数传递给service方法。Service方法通过servletRequest对象获得请求的信息。并处理该请求。再通过servletResponse对象生成这个请求的响应结果。然后销毁servletRequest和servletResponse对象。我们不管这个请求是post提交的还是get提交的,最终这个请求都会由service方法来处理。

销毁阶段:

当Web应用被终止时,servlet容器会先调用servlet对象的destrory方法,然后再销毁servlet对象,同时也会销毁与servlet对象相关联的servletConfig对象。我们可以在destroy方法的实现中,释放servlet所占用的资源,如关闭数据库连接,关闭文件输入输出流等。

在这里该注意的地方:

在servlet生命周期中,servlet的初始化和和销毁阶段只会发生一次,而service方法执行的次数则取决于servlet被客户端访问的次数

四,Servlet怎么处理一个请求?

当用户发送一个请求到某个Servlet的时候,Servlet容器会创建一个ServletRequst和ServletResponse对象。在ServletRequst对象中封装了用户的请求信息,然后Servlet容器把ServletRequst和ServletResponse对象传给用户所请求的Servlet,Servlet把处理好的结果写在ServletResponse中,然后Servlet容器把响应结果传给用户。

五,Servlet与JSP有什么区别?

  • 1.jsp经编译后就是servlet,也可以说jsp等于servlet。
  • 2.jsp更擅长页面(表现)。servlet更擅长逻辑编辑。 (最核心的区别)。
  • 3.在实际应用中采用Servlet来控制业务流程,而采用JSP来生成动态网页.在struts框架中,JSP位于MVC设计模式的视图层,而Servlet位于控制层。

六,Servlet里的cookie技术?

cookies是一种WEB服务器通过浏览器在访问者的硬盘上存储信息的手段,是由Netscape公司开发出来的。

cookie技术的好处:

  • 1.Cookie有效期限未到时,Cookie能使用户在不键入密码和用户名的情况下进入曾经浏览过的一些站点。
  • 2.Cookie能使站点跟踪特定访问者的访问次数、最后访问时间和访问者进入站点的路径。

七,Servlet里的过滤器?

1.过滤器的主要作用

  • 1)任何系统或网站都要判断用户是否登录。
  • 2)网络聊天系统或论坛,功能是过滤非法文字
  • 3)统一解决编码

2.怎么创建一个过滤器:

  • 1)生成一个普通的class类,实现Filter接口(javax.servlet.Filter;)
  • 2)重写接口里面的三个方法:init,doFilter,destroy。
  • 3)然后在web.xml配置过滤器。

八,Servlet里的监听器?

1.监听器的作用:自动执行一些操作。三种servlet监听器:对request的监听。对session的监听。对application的监听。

2.怎么创建一个session监听器:生成一个普通的class类,如果是对session的监听,则实现HttpSessionListener。然后重写里面的五个方法

public void sessionCreated(HttpSessionEvent arg0) {} // 创建
public void sessionDestroyed(HttpSessionEvent arg0) {} // 销毁
public void attributeAdded(HttpSessionEvent arg0) {} // 增加
public void attributeRemoved(HttpSessionEvent arg0) {} // 删除
public void attributeReplaced(HttpSessionEvent arg0) {} // 替换

这里有一个简单的Servlet示例Hello World! 点我进入:-D

]]>
Thu, 27 Dec 2012 13:19:33 +0000 viLuo http://viluo.compost/57
一个Servlet的简单示例Hello World! http://viluo.compost/56 这是一个关于Servlet的简单示例Hello World!

有时候在经常用Struts\Spring MVC这些优秀的MVC框架的时候,常常容易忘记这些底层简单的东西。本文纯粹做个备忘:-D

更多关于Servlet的知识,欢迎访问Servlet的相关的知识:-D

Java文件

public class HelloWorld extends HttpServlet {
private static final long serialVersionUID = -5676313618441583225L;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
writer.println("<html>");
writer.println("<head>");
writer.println("<title>Sample Application Servlet Page</title>");
writer.println("</head>");
writer.println("<body bgcolor=white>");
writer.println("Hello , wrold!");
writer.println("</body>");
writer.println("</html>");
}
}

web.xml配置

<!-- 定义servlet -->
<servlet>
<servlet-name>helloWorldServlet</servlet-name>
<servlet-class>com.viluo.servlet.HelloWorld</servlet-class>
</servlet>
<!-- 定义url和servlet的映射关系 -->
<servlet-mapping>
<servlet-name>helloWorldServlet</servlet-name>
<url-pattern>/hw</url-pattern>
</servlet-mapping>

访问地址

http://localhost<:port>/projectName/hw

]]>
Thu, 27 Dec 2012 13:09:21 +0000 viLuo http://viluo.compost/56
使用Eclipse构建Maven项目 http://viluo.compost/55 1. 安装m2eclipse插件

要用Eclipse构建Maven项目,我们需要先安装meeclipse插件

点击eclipse菜单栏Help->Eclipse Marketplace搜索到插件Maven Integration for Eclipse 并点击安装即可,如下图:

点击查看原图

安装成成之后我们在Eclipse菜单栏中点击File->New->Other,在弹出的对话框中会看到如下图所示:

点击查看原图

2. 构建Maven项目

以eclipse3.6为例

1)创建简单Maven项目

点击Eclipse菜单栏File->New->Ohter->Maven得到如下图所示对话框:

点击查看原图

选中Maven Project并点击Next,到下一个对话框继续点击Next得到如下对话框

点击查看原图

如图示操作,选择maven-archetype-quickstart,点击Next

点击查看原图

按图示填写好groupId, artfactId,version等信息,点击Finish。

点击查看原图

由此我们成功创建了一个简单的Maven项目,项目结构如图所示

2)创建Maven web项目

操作跟创建简单Maven项目类似,点击Eclipse菜单File->New->Other->Maven->Maven Project

在选择maven-archetype的界面进行如下操作:

点击查看原图

点击Next,填写好相应的groupId,artifactId,version等信息,点击Finish

得到的Maven web项目结构如下图所示:

点击查看原图

右击项目,点击Properties->Project Facets

点击查看原图

如上图可以看到项目为web2.3 java1.5 当然我们也可以改成我们所需要的版本,打开xml文件my-app-web/.settings/org.eclipse.wst.common.project.facet.core.xml,进行修改即可:

<?xml version="1.0" encoding="UTF-8"?>
<faceted-project>
<fixed facet="wst.jsdt.web"/>
<installed facet="java" version="1.5"/>
<installed facet="jst.web" version="2.3"/>
<installed facet="wst.jsdt.web" version="1.0"/>
</faceted-project>

3)导入Maven项目

在Eclipse project explorer中右击,在弹出框中选择import,得到如下图所示:

点击查看原图

选择Existing Maven Projects,并点击Next,得到如下图所示对话框:

点击查看原图

选择一个已经创建好的Maven项目,并点击Finish。

由此,导入Maven项目成功

3. 运行Maven命令

右击项目,点击Run as,如下图:

点击查看原图

即可看到有很多现有的maven命令,点击即可运行,并在控制台可以看到运行信息

如果你想运行的maven命令在这里没有找到,点击Maven build创建新的命令,操作如下图所示:

点击查看原图

如下图填入Maven命令,点击Run即可

点击查看原图

新增的maven命令可以通过如下方式找到,并再次运行:

点击查看原图

点击查看原图

碰到的问题

new Maven Project时候没有maven-archetype-quickstart这一项,可以找到maven-archetype-quickstart-1.1.jar[url:http://mirrors.ibiblio.org/maven2/org/apache/maven/archetypes/maven-archetype-quickstart/1.1/maven-archetype-quickstart-1.1.jar]的url,然后添加。如下图

点击查看原图

原文链接:使用Eclipse构建Maven项目

]]>
Thu, 13 Dec 2012 05:28:55 +0000 viLuo http://viluo.compost/55
emlog主题ublog-black V2.0发布,支持Emlog5 http://viluo.com/post/54 最近更新了ublog-black,支持emlog5,下载地址:http://www.emlog.net/template/242

制作比较匆忙,使用中发现问题可以跟贴,我会定期修复的

效果图:

首页&列表页

点击查看原图

内容页&评论分页

点击查看原图

 

ublog-black V1 下载地址:http://www.emlog.net/template/59,仅支持Emlog4

]]>
Thu, 15 Nov 2012 03:18:00 +0000 viLuo http://viluo.com/post/54
程序员之路——一个老程序员对刚上大学的学弟学妹的忠告 http://viluo.com/post/53 始终认为,对一个初学者来说,IT界的技术风潮是不可追赶。 我时常看见自己的DDMM们把课本扔了,去买些价格不菲的诸如C#, VB.Net 这样的大部头,这让我感到非常痛心。 而许多搞不清指针是咋回事的BBS站友眉飞色舞的讨论C#里面可以不用指针等等则让我觉得好笑。

C#就象当年的ASP 一样,“忽如一夜春风来,千树万树梨花开”,结果许多学校的信息学院成了“Web 学院”。 96, 97级的不少大学生都去做Web了。当然我没有任何歧视某一行业的意识。 我只是觉得如果他们把追赶这些时髦技术的时间多花一点在基础的课程上应该是可以走得更远的

几个误区

初学者对C#风潮的追赶其实也只是学习过程中经常遇到的几个误区之一。 我将用一些实际的例子来说明这些现象,你可以按部就班的看看自己是不是属于其中的一种或者几种:

认为计算机技术等于编程技术:

有些人即使没有这个想法,在潜意识中也有这样的冲动。 让我奇怪的是,许多信息学院的学生也有这样的念头。 认为计算机专业就是编程专业,与编程无关的,或者不太相关课程他统统都不管,极端的学生只要书上没带“编程”两个字他就不看。

其实编程只是计算机技术应用过程中一种复杂性最低的劳动,这就是为什么IT业最底层的人是程序员(CODER)。 计算机技术包括了多媒体,计算机网络,人工智能,模式识别,管理信息系统等等这些方面。 编程工作只是在这些具体技术在理论研究或者工程实践的过程中表达算法的过程。

编程的人不一定对计算机技术的了解就一定很高。而一个有趣的现象是,不少大师级的计算机技术研究者是不懂编程的。 网上的炒作和现实中良好的工作待遇把编程这种劳动神秘化了。 其实每一个程序员心里都明白,自己这些东西,学的时候并不比其它专业难,所以自然也不会高档到哪里去。

咬文嚼字的孔已己作风:

我见过一本女生的《计算机网络原理》教材,这个女生像小学生一样在书上划满了横杠杠,笔记做得满满的,打印出来一定比教材还厚。 我不明白的是,像计算机网络原理这样的课程有必要做笔记? 我们的应试教育的确害了不少学生,在上《原理》这一类课程的时候许多学生像学《马列原理》一样逐字背诵记忆。 这乃是我见过的最愚蠢的行为。 所谓《原理》,即是需要掌握它为什么这样做,学习why,而不是how(怎样做)。 极端认真的学生背下以太网的网线最大长度,数据帧的长度,每个字段的意义,IP报头的格式等等,但是忘了路由的原则,忘了TCP/IP协议设计的宗旨。 总之许多人花了大量的时间把书背得滚瓜烂熟却等于什么也没学。

在学习编程的时候这些学生也是这样,他们确切的记得C++语法的各个细节。 看完了C++ 教程后看《Thinking in C++》(确实是好书),《Inside C++》,《C++ reference》,this C++, that C++……, 然后是网上各种各样的关于C++语法的奇闻逸事,然后发现自己又忘了C++的一些语法,最后回头继续恶补…。

有个师弟就跟我说:“C++太难了,学了这里忘了那里,学了继承忘了模板。”

我的回答道:“你不去学就容易了”。

我并没有教坏他,只是告诉他,死抠C++的语法就和孔已己炫耀茴香豆的茴字有几种写法一样毫无意义。 你根本不需要对的C++语法太关心,动手编程就是了,有不记得的地方一查MSDN就立马搞定。 我有个结论就是,实际的开发过程中对程序语法的了解是最微不足道的知识。 这是为什么我在为同学用Basic(我以前从没有学过它)写一个小程序的时候,只花了半个小时看了看语法,然后再用半个小时完成了程序, 而一个小时后我又完全忘记了Basic 的所有关键字。

不顾基础,盲目追赶时髦技术:

终于点到题目上来了。 大多数的人都希望自己的东西能够马上跑起来,变成钱。 这种想法对一个已经进入职业领域的程序员或者项目经理来说是合理的,而且IT技术进步是如此的快,不跟进就是失业。 但是对于初学者来说(尤其是时间充裕的大中专在校生),这种想法是另人费解的。

一个并未进入到行业竞争中来的初学者最大的资本便是他有足够的时间沉下心来学习基础性的东西,学习why而不是how。 时髦的技术往往容易掌握,而且越来越容易掌握,这是商业利益的驱使,为了最大化的降低软件开发的成本。 但在IT领域内的现实就是这样,越容易掌握的东西,学习的人越多,而且淘汰得越快。 每一次新的技术出来,都有许多初学者跟进,这些初学者由于缺乏必要的基础而使得自己在跟进的过程中花费大量的时间,而等他学会了,这种技术也快淘汰了。

基础的课程,比方数据结构,操作系统原理等等虽然不能让你立马就实现一个Linux(这是许多人嘲笑理论课程无用的原因), 但它们能够显著的减少你在学习新技术时学习曲线的坡度。 而且对于许多关键的技术(比方Win32 SDK 程序的设计,DDK的编程)来说甚至是不可或缺的。

一个活生生的例子是我和我的一个同学,在大一时我还找不到开机按纽,他已经会写些简单的汇编程序了。 我把大二的所有时间花在了汇编,计算机体系结构,数据结构,操作系统原理等等这些课程的学习上,而他则开始学习HTML和VB,并追赶ASP的潮流。 大三的时候我开始学习Windows 操作系统原理,学习SDK编程,时间是漫长的,这时我才能够用VC开发出象模象样的应用程序。 我曾一度因为同学的程序已经能够运行而自己还在学习如何创建对话框而懊恼不已,但临到毕业才发现自己的选择是何等的正确。 和我谈判的公司开出的薪水是他的两倍还多。

下面有一个不很恰当的比方:假设学习VB编程需要 4个月,学习基础课程和VC的程序设计需要1年。 那么如果你先学VB,再来学习后者,时间不会减少,还是1年,而反过来,如果先学习后者,再来学VB,也许你只需要1个星期就能学得非常熟练。

几个重要的基础课程

如果你是学生,或者如果你有充足的时间。我建议你仔细的掌握下面的知识。 我的建议是针对那些希望在IT技术上有所成就的初学者。 同时我还列出了一些书目,这些书应该都还可以在书店买到。 说实在的,我在读其他人的文章时最大的心愿就是希望作者列出一个书单。

大学英语 -不要觉得好笑。我极力推荐这门课程是因为没有专业文档的阅读能力是不可想象的。 中文的翻译往往在猴年马月才会出来,而现在的许多出版社干脆就直接把E文印刷上去。 学习的方法是强迫自己看原版的教材,开始会看不懂,用多了自然熟练。 吃得苦下得狠心绝对是任何行业都需要的品质。

计算机体系结构和汇编语言 -关于体系结构的书遍地都是,而且也大同小异, 倒是汇编有一本非常好的书《80x86汇编语言程序设计教程》(清华大学出版社,黑色封面,杨季文著)。 你需要着重学习386后保护模式的程序设计。否则你在学习现代操作系统底层的一些东西的时候会觉得是在看天书。

计算机操作系统原理 -我们的开发总是在特定的操作系统上进行,如果不是,只有一种可能:你在自己实现一个操作系统。 无论如何,操作系统原理是必读的。这就象我们为一个芯片制作外围设备时,芯片基本的工作时序是必需了解的。 这一类书也很多,我没有发现哪一本书非常出众。 只是觉得在看完了这些书后如果有空就应该看看《Inside Windows 2000》(微软出版社,我看的是E文版的,中文的书名想必是Windows 2000 技术内幕之类吧)。 关于学习它的必要性,ZDNET上的另一篇文章已经有过论述。

数据结构和算法 -这门课程能够决定一个人程序设计水平的高低,是一门核心课程。我首选的是清华版的(朱战立,刘天时)。 很多人喜欢买C++版的,但我觉得没有必要。C++的语法让算法实现过程变得复杂多了,而且许多老师喜欢用模块这一东西让算法变得更复杂。 倒是在学完了C版的书以后再来浏览一下 C++的版的书是最好的。

软件工程 -这门课程是越到后来就越发现它的重要,虽然刚开始看时就象看马哲一样不知所云。 我的建议是看《实用软件工程》(黄色,清华)。 不要花太多的时间去记条条框框,看不懂就跳过去。 在每次自己完成了一个软件设计任务(不管是练习还是工作)以后再来回顾回顾,每次都会有收获。

Windows 程序设计 -《北京大学出版社,Petzold著》我建议任何企图设计Windows 程序的人在学习VC以前仔细的学完它。 而且前面的那本《Inside Windows 2000》也最好放到这本书的后面读。 在这本书中,没有C++,没有GUI,没有控件。有的就是如何用原始的C语言来完成Windows 程序设计。 在学完了它以后,你才会发现VC其实是很容易学的。千万不要在没有看完这本书以前提前学习VC,你最好碰都不要碰。 我知道的许多名校甚至都已经用它作为教材进行授课。可见其重要。

上面的几门课程我认为是必学的重要课程(如果你想做Windows 程序员)

对于其它的课程有这样简单的选择方法:如果你是计算机系的,请学好你所有的专业基础课。 如果不是,请参照计算机系的课程表。 如果你发现自己看一本书时无法看下去了,请翻到书的最后,看看它的参考文献,找到它们并学习它们,再回头看这本书。 如果一本书的书名中带有“原理”两个字,你一定不要去记忆它其中的细节,你应该以一天至少50页的速度掌握其要领。 尽可能多的在计算机上实践一种理论或者算法。

日三省乎己

每天读的书太多,容易让人迷失方向。看看我以前整理的一个合格程序员应该看哪些书,如果原文被墙,可以到CSDN看。 一定要在每天晚上想想自己学了些什么,还有些什么相关的东西需要掌握,自己对什么最感兴趣,在一本书上花的时间太长还是不够等等。 同时也应该多想想未来最有可能出现的应用,这样能够让你不是追赶技术潮流而是引领技术潮流。 同时,努力使用现在已经掌握的技术和理论去制作具有一定新意的东西。 坚持这样做能够让你真正成为一个软件“研发者”而不仅仅是一个CODER

把最多的时间花在学习上

这是对初学者最后的忠告。 把每个星期玩CS或者CS的时间压缩到最少,不玩它们是最好的。 同时,如果你的ASP技术已经能够来钱,甚至有公司请你兼职的话,这就证明你的天分能够保证你在努力的学习之后取得更好的收益,你应该去做更复杂的东西。 眼光放长远一些,这无论是对谁都是适用的。

原文链接:程序员之路——一个老程序员对刚上大学的学弟学妹的忠告

]]>
Mon, 22 Oct 2012 07:58:17 +0000 viLuo http://viluo.com/post/53
Eclipse和DOS下运行程序的差异(主要涉及user.dir和classpath的问题) http://viluo.com/post/52 遇到过很多人,甚至工作了几年的人,经常搞不清classpath,写了一个小程序, Eclipse能运行的程序,在DOS下就经常运行不了了,一般都是报这样的异常:java.lang.ClassNotFoundException。群里一旦有人问起,一帮人看都不看,一般都由以下几种说法:

  • 1. 设置了环境变量没? 这种几率还真小, 安装了jdk基本上毫无疑问都会设置环境变量.
  • 2. java文件的问题. 这个完全就是在胡扯了
  • 3. 先把java文件编译一下. 这个都在eclilise能运行了. 已经都编译好了,无需再多此一举。
  • 4. classliath路径有问题. 这个还真要稍微注意一下. 如果是jdk5.0及以后的版本, 那么classliath是完全可以不用设置的.现在大部分人应该都不会再使用jdk1.4或者更老的版本了吧. 所以这个概率也很小.
  • 5. 把环境变量重新设置一下. 这个???
  • 6. 这个问题太多了,百度去吧. 我想这么说的人估计自己也搞不清楚。很多人都习惯了Eclilise,怎么在DOS下编译和运行还真不熟悉.

首先说明一下, 上面的程序是带有包名的, 很好解决, 只要进入到项目的bin目录下, 运行java 报名.类名 即可.

下面的程序稍微有点区别.也涉及到相对路径的问题. 项目结构如下:

点击查看原图

IDEAndDosTest.java的代码如下(不要去揪程序的规范):

public static void main(String[] args) throws IOException {
FileInputStream in = new FileInputStream("src/iotest/test.txt");
int data;
while((data = in.read()) != -1) {
System.out.print(data + " ");
}
in.close();
}

很简单, 就是读取一个文件, 打印字节.

在Eclipse下运行当然是没有任何问题的.

在DOS下运行,进入到的目录是bin. 这时候运行发现, 报异常了:

Exception in thread "main" java.io.FileNotFoundException: src\iotest\test.txt (系统找不到指定的路径。)

异常也很明显, 文件路径不对, 没错, 路径当然不对了. 既然现在在bin目录下, 那么肯定相对应bin目录了.

这时需要搞清楚的就是两个系统属性"user.dir"和"java.class.path".

GetClassPathAndUserDir类的代码很简单:

System.out.println(System.getProperty("user.dir"));
System.out.println(System.getProperty("java.class.path"));

在Eclipse下运行, 结果是:

user.dir = 磁盘路径\Test
java.class.path = 磁盘路径\Test\bin

在DOS下运行(目录是bin), 结果是:

user.dir = 磁盘路径\Test\bin
java.class.path = . //  其实就是磁盘路径\Test\bin

从以上的结果其实已经可以看出结果了。问题主要集中在"user.dir".

其实解决上面的问题也很简单. 换种方式, DOS下进入项目的目录, 也就是现在在Test目录下. 运行

java -cp bin iotest.IDEAndDosTest

OK. 没有任何问题.

其实这个时候user.dir已经发生了变化. 其实GetClassPathAndUserDir这个类在不同的目录下运行结果是有些差异的

比如,现在在Test目录下运行,结果是:

user.dir = 磁盘路径\Test
java.class.path = bin

来个更好玩的, 直接在E盘下 运行,结果是:

user.dir = E:\
java.classpath = E:\Java\workspace\framework_workspace\Test\bin

可以发现java.classpath是不变的, 而user.dir是不断变化的.

不过要是在E盘下运行IDEAndDosTest,一样还是会报java.io.FileNotFoundException

从上面的结果也可以看出了"user.dir", 可以简单的说就是当前用户的目录.

把IDEAndDosTest.java稍作修改:

FileInputStream in = new FileInputStream("iotest/test.txt");  

这个时候在DOS下(bin目录), 运行时没有任何的, 在Eclipse运行会报 java.io.FileNotFoundException

不过只要我们稍作修改就能让其运行正常:

点击查看原图

做如下的修改运行也就完全没问题了.

既然这两种方式都这么麻烦, 那有没有什么好的替换方式呢 ?当然有了:

InputStream in = IDETest.class.getResourceAsStream("test.txt"); // 当前类的同目录下 

或者

InputStream in = IDEAndDosTest.class.getClassLoader().getResourceAsStream("iotest/test.txt"); // classpath目录  

现在不管你怎么运行, 都没有问题了(当然了你要指定好classpath)

其实也可以看出了如果要用绝对路径, 千万别涉及到"user.dir"的问题, 最好相对于你的classpath而言

原文链接:Eclipse和DOS下运行程序的差异(主要涉及user.dir和classpath的问题)

]]>
Mon, 22 Oct 2012 07:37:01 +0000 viLuo http://viluo.com/post/52
Java反编译工具JD http://viluo.com/post/51 java反编译的内核一般用的都是jad,这次发现一个比较强悍的,jd,法国人做的,有自己的内核,也有配套的可视化工具,有Eclipse的插件,甚至连IntelliJ都有!

官方地址:http://java.decompiler.free.fr/

JD-GUI下载地址:http://java.decompiler.free.fr/?q=jdgui#downloads

JD-Eclipse下载地址:http://java.decompiler.free.fr/?q=jdeclipse#install

或直接使用Update Site:http://java.decompiler.free.fr/jd-eclipse/update

JD-IntelliJ下载地址:http://java.decompiler.free.fr/?q=jdintellij

JD-GUI界面:

点击查看原图

JD-Eclipse界面:

点击查看原图

JD-IntelliJ界面:

点击查看原图

]]>
Fri, 21 Sep 2012 01:38:24 +0000 viLuo http://viluo.com/post/51
Java derby数据库入门 http://viluo.com/post/50 1.derby介绍

derby是一个用java语言写的开源数据库,它可以作为一个嵌入式数据库嵌入在应用程序中。在没有数据库环境的情况下,或者在做数据迁移,或者在一些相关的数据存储中使用

2.下载derby

下载地址:http://db.apache.org/derby/derby_downloads.html

3.配置derby

这里使用嵌入式的方式访问数据库,只需要把jar包derby.jar放到应用中就可以了

4.编写代码

public static void main(String[] args) {
try {
// 加载驱动
Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance();
// 连接数据库
Connection conn = DriverManager.getConnection("jdbc:derby:mydb;create=true");
Statement st = conn.createStatement();
// 建表
st.execute("create table CITY (ID INT NOT NULL,CITYNAME VARCHAR(10) NOT NULL)");
// 插入数据
st.executeUpdate("insert into CITY(ID,CITYNAME) values (1,'北京')");
// 插入数据
st.executeUpdate("insert into CITY(ID,CITYNAME) values (2,'上海')");
// 读取刚插入的数据
ResultSet rs = st.executeQuery("select * from CITY");
while (rs.next()) {
int id = rs.getInt(1);
String cityName = rs.getString(2);
System.out.println("ID=" + id);
System.out.println("CITYNAME=" + cityName);
}
rs.close();
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}

mydb是数据库的名字,没有这个数据库的话会自动建立。建立完数据库后,会在应用的根目录下建立mydb的文件夹,里面有很多文件,用来保存数据。

]]>
Thu, 20 Sep 2012 11:42:18 +0000 viLuo http://viluo.com/post/50
第一个计算机BUG http://viluo.com/post/49 对于开发人员来说,Bug 是无法避免也最令人头疼的,你需要不停地 Debug、不停地 Debug……

Bug 一词的原意是“臭虫”或“虫子”。现在,在电脑系统或程序中,如果隐藏着的一些未被发现的缺陷或问题,也称之为“Bug”,这是怎么回事呢?

第一个有记载的 Bug 是美国海军编程员、编译器的发明者格蕾斯·哈珀(GraceHopper)发现的。哈珀后来成为了美国海军的一位将军,还领导了著名计算机语言 Cobol 的开发。

1945年 9 月 9 日,下午三点。哈珀中尉正领着她的小组构造一个称为“马克二型”的计算机。这还不是一个真正的电子计算机,它使用了大量的继电器,一种电子机械装置。第二次世界大战还没有结束。哈珀的小组日以继夜地工作。机房是一间第一次世界大战时建造的老建筑。那是一个炎热的夏天,房间没有空调,所有窗户都敞开散热。

突然,马克二型死机了。技术人员试了很多办法,最后定位到第 70 号继电器出错。哈珀观察这个出错的继电器,发现一只飞蛾躺在中间,已经被继电器打死。她小心地用摄子将蛾子夹出来,用透明胶布帖到“事件记录本”中,并注明“第一个发现虫子的实例”。

点击查看原图

从此以后,人们将计算机错误戏称为 Bug,与之相对应,人们将发现 Bug 并加以纠正的过程叫做“Debug”,意即“捉虫子”或“杀虫子”。遗憾的是,在中文里面,至今仍没有与“Bug”准确对应的词汇,于是只能直接引用“Bug”一词。

]]>
Mon, 10 Sep 2012 08:39:01 +0000 viLuo http://viluo.com/post/49
程序员常说的20句话 http://viluo.com/post/48 viLuo:呃,被戳中了...程序猿们,看看哪句踩住了你的痛角~~

1.见鬼了….

2.以前重来没出过问题。

3.昨天还好好的。

4.这怎么可能?

5.一定是机器出了问题。

6.你究竟输入什么了导致它崩溃?

7.应该是数据有问题。

8.我好几个星期都没碰这块代码了!

9.你一定弄错了版本。

10.怎么会就这么巧。

11.我不可能把所有的问题都测到。

12.这不可能是因为它引起的。

13.本来好用的,只是没测试过。

14.肯定是有人改了我的代码。

15.查查你机器上有没有病毒。

16.even though it doesn’t work, how does it feel?

17.你的机器上不能使用这个版本。

18.为什么你会觉得应该出那个结果才是正确的?

19.程序出问题时你在干什么了?

20.这问题我改了呀!

原文链接:程序员最常说的20句话

]]>
Mon, 10 Sep 2012 08:16:01 +0000 viLuo http://viluo.com/post/48
AOP的简单入门 http://viluo.com/post/47 viLuo:不得不说,inter12这位家伙真是一位淫才,淫得一手好湿。这是他的博客地址:http://inter12.iteye.com/

自己也算是从业多年,对于AOP的概念应该算是听的烂的不能再烂了,这方面的书也看的不少,但是自己一直没有机会去实践下。

乘在这个稍微有点空闲的下午,就随手玩玩SPRING的AOP,也谈谈自己对于AOP的理解及其衍生的一些东西。

1.一切术语都是纸老虎

基本概念,也可以说是基本术语。任何一个软件概念提出时候,都少不了这个东西。CRM,AOP,SOA等等,伴随这些东西的都会有相应体系内的术语。

我个人的看法是一切术语的出现不是并不是向大众解释清楚这件事到底是怎么一回事,其主要是基于两个方面考虑:

(1)让自己提出观点显得系统化,更具有说服力。

(2)迷惑大众,或是迷糊那些刚进入这个领域的初学者。

两个看似矛盾的因素,其实归结到本质就是将一个简单的东西复杂化并迷糊那些辨别能力的一般的人,大家对于抽象到一定程度的东西但是心怀敬畏之心的,然后带着膜拜的心理去接受,生怕一不小心亵渎了内心的女神。扯开来讲现在社会,官员的道德沦丧,其中的一个诱因就是对于敬畏之心的缺失,当一个人无所畏时,才是最可怕的,因为这个时候已经没有任何约束能约束他的行为。

回归正题,既然提到术语,那么我们就将AOP中的那些术语列出来看看。

切面(Aspect)、连接点(Joinpoint)、通知(Advice)、切入点(Pointcut) 、目标对象(Target Object)、AOP代理(AOP Proxy)。

通知又分为几种:前置通知(Before advice)、后通知(After advice)、返回后通知(After return advice)、环绕通知(Around advice)、抛出异常后通知(After throwing advice) 等。

好了,现在我们来看看这些术语,谁能一眼就明白这些东西能告诉我们什么?谁能通畅的理清楚它们之间的关系。开始解释之前,我们看看维基百科上对AOP的定义是什么:

面向侧面的程序设计(aspect-oriented programming,AOP,又译作面向方面的程序设计、觀點導向編程)是计算机科学中的一个术语,指一种程序设计范型。该范型以一种称为侧面(aspect,又译作方面)的语言构造为基础,侧面是一种新的模块化机制,用来描述分散在对象、类或函数中的横切关注点(crosscutting concern)。

多么简单的解释,就是对于那些在主程序业务之外的事情,我们该怎么设计,看清楚,是程序设计,而不是程序编码,很多地方都将AOP理解成面向切面的编码,那是错误,AOP约定的是一种设计范型,具体到java上的实现例如aspectJ,例如spring的AOP。再具体到实现技术就是JDK自带的动态代理,cglib的字节码修改等等。AOP != spring AOP ,AOP != cglib 。一个是设计范型,一个是实现。

(这里扯开点说,一些所谓的架构师喜欢谈概念,还有人提出架构师更关注抽象的东西,普通的码农更关注具象的东西。这些东西本身没错,在大量的实践之后,我们确实应该去在这大量的实践中归纳,总结出规律,这就是进行抽象。但是,但是,那些只跟你扯抽象而不落地的架构师,还是远离些,因为他们不是基于大量的具象后,进行抽象,他们不过是邯郸学步的抄袭那些真正的架构师的想法,并转变为自己的观点,TMD就是个大忽悠)

2.那些主程序之外的杂事

我们知道,一个程序是会被编译成计算机能识别的机器码,交给机器去执行,那么我们想知道机器在执行我们的代码时候,发生的一些事,例如:

一个输入是否得到我们想要的输出呢?

一个函数执行的时间开销是多少呢?

若是一个良好的程序遇到无法处理的异常我们该怎么办呢?

存在多个数据源,我们需要在多个数据源的更新都完成后,再提交该怎么处理呢?

我们想将一些我们不认可的请求拒绝,那又该怎么处理呢?

当以上的这些杂事出现时,我们该怎么做呢,一种很简单粗暴的方式就是硬编码的将这些杂事写入到我们的主要业务程序中,我想这种方式大家在日常的开发经常能看到,包括你去看一些所谓平台级别的产品也是采用这种方式,笔者现在所在单位的部分产品也是如此。不管你爽不爽,老子爽了就可以了。

3.上吧,骚年

用个不恰当的比喻来说:你是个开饭店的,来了很多顾客,当你女服务员在招待顾客时,你突然发现北大院长来了,想来OOXX下。你就定了一个流程,来北大院长,可OOXX,硬编码的方式就是:

(1)你女服务员在招待

(2)北大院长跟她OOXX

(3)你女服务员在招待

(4)屌丝来了,无视之

......

这个过程中你不管你女服务员是否来例假,你不关心你女服务员是否今天心情不好。嗯,院长爽了,若是你得到了某种回报,也还好,爽了;若是什么都得不到,那可就欲哭无泪。

如果你足够幸福的话,有另一个口味美女服务员,她会隐藏技能---洗脚。恰巧经管学院院长也来了,这个家伙还喜欢洗脚,那怎么办,那就上吧:

(1)你女服务员在招待

(2)北大院长跟她OOXX+洗脚

(3)你女服务员在招待

(4)屌丝来了,无视之

......

如果你生意足够好,恰好北大院长又很多,好吧,你的美女们一直处在XXOO状态中..... 很high,很happy。但是,我们回到最开始,你为什么要招女服务员?找她们来,是因为你需要她们去招待顾客,一个屌丝在等吃饭没关系,若是一群的屌丝在等吃饭,你就悲剧,没人招待屌丝们了,因为你的那些服务员都在跟院长们OOXX中,因为命令已经固化到流程中,你改不了,至少在你修改流程之前。通过我们软件术语来说,就是不能及时、灵活的应对自身内部(你的美女们身体、心情)和外部(屌丝数量)的变化。当然, 你若是铁道部这样的共和国长子,那是没关系的,让那群屌丝们等去吧,因为方圆960万平方公里就此一家,别无分号。

若你不是,哪天觉得自己有点生意有点扛不住或是那点生殖器破事被某个黑心,吃不到葡萄的院长小弟揭发了,扛不住随之而来的社会舆论压力,不能跟院长们OOXX了,只准对他们笑个,这个时候你得通知那些女服务员,说不准OOXX了,只能看了。若是你只有一家店,还好,自己喊一声,重新打印流程规章表,若是全国连锁的话...... 一桌的杯具摆在你茶几上。

4.正义化身的出现

好了,扯了这么多,终于要该AOP兄弟出场了,再不出估计戏都散场了。

针对以上的种种问题,我们该怎么处理这些我们店主要生意之外的杂事呢(OOXX),有什么更好的方式来随时应对种种变化。这个就是我们AOP兄弟想干的事情,从主业中剥离出这些杂事,进行单独处理的设计。主业务只关注于自己的领域,对于特殊领域的处理(OOXX),通过侧面来封装维护,这样使得散落在不同口味美女的特殊操作可以很好的管理起来。来段专业点的说明吧:

从主关注点中分离出横切关注点是面向侧面的程序设计的核心概念。分离关注点使得解决特定领域问题的代码从业务逻辑中独立出来,业务逻辑的代码中不再含有针对特定领域问题代码的调用,业务逻辑同特定领域问题的关系通过侧面来封装、维护,这样原本分散在在整个应用程序中的变动就可以很好的管理起来。

好了,AOP的就是个这么简单的东西,别去想那些繁杂的spring配置和概念术语。它只是一种设计范型。

绕了这么久,让我们来打倒那些纸老虎吧。

我开饭店,屌丝、院长来吃饭,美女们招待顾客 ,这个是我们的主业。 ======== 目标对象(Target Object) 就是店主我,我开了两个店,戏院和饭店

哦,北大院长来饭店吃饭了 ======== 切入点(Pointcut) 他们来我戏院看戏的话,不管,直管饭店的事

院长开始吃饭,喝酒了。 ======== 连接点(Joinpoint) ,就是我们的一些行为,院长如果来围观的话,无视之,哥是开饭店的。

院长想跟美女们OOXX了 ======== 通知(Advice)院长来了,也吃了饭了,那接下来干什么呢?通知就是决定干什么:OOXX或是洗脚

院长除了想OOXX之外,还想洗脚,那么怎么办呢? ======== 切面(Aspect) ,规定院长来了可以干什么,就是决定可以有多少个通知:OOXX||洗脚 或是 OOXX && 洗脚

--------------------------------------------------------------------------------------------------

院长想吃饭后洗脚 ======== 后通知(After advice)

院长想吃饭前洗脚 ======== 前置通知(Before advice)

院长想根据吃饭后的心情决定是OOXX还是洗脚 ======== 返回后通知(After return advice)

院长吃饭吃出脑中风了 ======== 抛出异常后通知(After throwing advice)这个时候有个通知跳出来:打120,送医院!

院长想饭前洗脚,饭后OOXX ======== 环绕通知(Around advice)

作为老板的我,应该怎么更好的切入这些洗脚啊,OOXX服务呢 ======== AOP代理(AOP Proxy)怎么在干好招待顾客这件事上切入 洗脚||OOXX

若是上面的这些你还是看不明白的话,那么我们就具象到spring上,看看到底是件上面事吧。spring中Aspect叫Advisor。Joinpoint叫Weaving。很操蛋,也很让人无语的术语啊

切面:
package com.zhaming.aop.advice;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
/**
* 后置通知
* @author inter12
*
*/
public class AfterAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("拦截了:" + method.getName() + "方法");
System.out.println("洗脚");
}
}
package com.zhaming.aop.advice;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
/**
* 前置通知
*
* @author inter12
*/
public class BeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("拦截了:" + method.getName() + "方法");
System.out.println("OOXX");
}
}
package com.zhaming.aop.advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
* 环绕通知
*
* @author inter12
*/
public class CompareAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object result = null;
String userName = invocation.getArguments()[0].toString();
if (null != userName && userName.equals("yuanzhang")) {
System.out.println("院长通过---------------");
result = invocation.proceed();
} else {
System.out.println("屌丝拒绝---------------");
}
return result;
}
}
目标对象:
package com.zhaming.aop.restaurant;
public interface RestaurantService {
public void zhaodaiguke(String userName);
public void weiguan(String userName);
}
package com.zhaming.aop.restaurant;
/**
* 目标对象
*
* @author inter12
*/
public class RestaurantServiceImpl implements RestaurantService {
@Override
public void zhaodaiguke(String userName) {
System.out.println("--------- 姑娘们在招待顾客:" + userName);
}
@Override
public void weiguan(String userName) {
System.out.println(userName + ":在围观");
}
}
客户端:
package com.zhaming.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import com.zhaming.aop.restaurant.RestaurantService;
public class Main {
public static void main(String[] args) {
ApplicationContext applicationContext = new FileSystemXmlApplicationContext(
"//home/inter12/workspace/Light/src/main/java/appcontext-aop.xml");
RestaurantService bean = (RestaurantService) applicationContext.getBean("restaurantService");
bean.zhaodaiguke("yuanzhang");
bean.zhaodaiguke("diaosi");
}
}
配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!-- 通知 -->
<bean id="beforeAdvice" class="com.zhaming.aop.advice.BeforeAdvice"></bean>
<bean id="afterAdvice" class="com.zhaming.aop.advice.AfterAdvice"></bean>
<bean id="compareAdvice" class="com.zhaming.aop.advice.CompareAdvice"></bean>
<!-- 目标对象 -->
<bean id="restaurantServiceTarget" class="com.zhaming.aop.restaurant.RestaurantServiceImpl"></bean>
<bean id="restaurantService" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 拦截那些接口 : 切入点 只关心饭店的事-->
<property name="proxyInterfaces">
<value>com.zhaming.aop.restaurant.RestaurantService</value>
</property>
<!-- 对这些方式做那些拦截:切面 -->
<property name="interceptorNames">
<list>
<!--
<value>beforeAdvice</value> <value>afterAdvice</value>
-->
<value>compareAdvice</value>
</list>
</property>
<property name="target">
<ref bean="restaurantServiceTarget" />
</property>
</bean>
</beans>

5.AOP能干什么?

现在回头看看最初问的五个问题,那些杂事,是不是可以对应到软件中的几个概念:日志记录,性能统计,安全控制,事务处理,异常处理,更衍生的提还有缓存,持久化,同步等

原文链接:AOP的简单入门

]]>
Mon, 10 Sep 2012 07:45:34 +0000 viLuo http://viluo.com/post/47
java编程中'为了性能'一些尽量做到的地方 http://viluo.com/post/46 viLuo:这里面有精华,有糟粕,需要大家根据自己的实际开发经验取舍了。

最近的机器内存又爆满了,出了新增机器内存外,还应该好好review一下我们的代码,有很多代码编写过于随意化,这些不好的习惯或对程序语言的不了解是应该好好打压打压了。

下面是参考网络资源和总结一些在java编程中尽可能做到的一些地方

1.尽量在合适的场合使用单例

使用单例可以减轻加载的负担,缩短加载的时间,提高加载的效率,但并不是所有地方都适用于单例,简单来说,单例主要适用于以下三个方面

第一,控制资源的使用,通过线程同步来控制资源的并发访问

第二,控制实例的产生,以达到节约资源的目的

第三,控制数据共享,在不建立直接关联的条件下,让多个不相关的进程或线程之间实现通信

2.尽量避免随意使用静态变量

要知道,当某个对象被定义为stataic变量所引用,那么gc通常是不会回收这个对象所占有的内存,如

public class A {
static B b = new B();
}

此时静态变量b的生命周期与A类同步,如果A类不会卸载,那么b对象会常驻内存,直到程序终止。

3.尽量避免过多过常的创建java对象

尽量避免在经常调用的方法,循环中new对象,由于系统不仅要花费时间来创建对象,而且还要花时间对这些对象进行垃圾回收和处理,在我们可以控制的范围内,最大限度的重用对象,最好能用基本的数据类型或数组来替代对象。

4.尽量使用final修饰符

带 有final修饰符的类是不可派生的。在Java核心API中,有许多应用final的例子,例如java.lang.String。为String类指 定final防止了使用者覆盖length()方法。另外,如果一个类是final的,则该类所有方法都是final的。java编译器会寻找机会内联 (inline)所有的final方法(这和具体的编译器实现有关)。此举能够使性能平均提高50%。

5.尽量使用局部变量

调用方法时传递的参数以及在调用中创建的临时变量都保存在栈(Stack)中,速度较快。其他变量,如静态变量,实例变量等,都在堆(Heap)中创建,速度较慢。

6.尽量处理好包装类型和基本类型两者的使用场所

虽然包装类型和基本类型在使用过程中是可以相互转换,但它们两者所产生的内存区域是完全不同的,基本类型数据产生和处理都在栈中处理,包装类型是对象,是在堆中产生实例。

在集合类对象,有对象方面需要的处理适用包装类型,其他的处理提倡使用基本类型。

7.慎用synchronized,尽量减小synchronize的方法

都知道,实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。synchronize方法被调用时,直接会把当前对象锁了,在方法执行完之前其他线程无法调用当前对象的其他方法。所以synchronize的方法尽量小,并且应尽量使用方法同步代替代码块同步。

8.尽量使用StringBuilder和StringBuffer进行字符串连接

这个就不多讲了

9.尽量不要使用finalize方法

实际上,将资源清理放在finalize方法中完成是非常不好的选择,由于GC的工作量很大,尤其是回收Young代内存时,大都会引起应用程序暂停,所以再选择使用finalize方法进行资源清理,会导致GC负担更大,程序运行效率更差。(其实不推荐用finalize方法的根本原因在于,JVM的规范并不保证何时执行该方法,所以用这个方法来释放资源很不合适,有可能造成长时间资源得不到释放。)

10.尽量使用基本数据类型代替对象

String str = "hello";

上面这种方式会创建一个“hello”字符串,而且JVM的字符缓存池还会缓存这个字符串;

String str = new String("hello");

此时程序除创建字符串外,str所引用的String对象底层还包含一个char[]数组,这个char[]数组依次存放了h,e,l,l,o

11.单线程应尽量使用HashMap, ArrayList

HashTable,Vector等使用了同步机制,降低了性能。

12.尽量合理的创建HashMap

当你要创建一个比较大的hashMap时,充分利用另一个构造函数

public HashMap(int initialCapacity, float loadFactor)

避免HashMap多次进行了hash重构,扩容是一件很耗费性能的事,在默认中initialCapacity只有16,而loadFactor是 0.75,需要多大的容量,你最好能准确的估计你所需要的最佳大小,同样的Hashtable,Vectors也是一样的道理。

13.尽量减少对变量的重复计算

for (int i = 0; i < list.size(); i++) {
}

应该改为

for (int i = 0, len = list.size(); i < len; i++) {
}

并且在循环中应该避免使用复杂的表达式,在循环中,循环条件会被反复计算,如果不使用复杂表达式,而使循环条件值不变的话,程序将会运行的更快。

14.尽量避免不必要的创建

A a = new A();
if (i == 1) {
list.add(a);
}

应该改为

if (i == 1) {
A a = new A();
list.add(a);
}

15.尽量在finally块中释放资源

程序中使用到的资源应当被释放,以避免资源泄漏。这最好在finally块中去做。不管程序执行的结果如何,finally块总是会执行的,以确保资源的正确关闭。

16.尽量确定StringBuffer的容量

StringBuffer 的构造器会创建一个默认大小(通常是16)的字符数组。在使用中,如果超出这个大小,就会重新分配内存,创建一个更大的数组,并将原先的数组复制过来,再丢弃旧的数组。在大多数情况下,你可以在创建 StringBuffer的时候指定大小,这样就避免了在容量不够的时候自动增长,以提高性能。如:

StringBuffer buffer = new StringBuffer(1000);

17.尽量早释放无用对象的引用

大部分时,方法局部引用变量所引用的对象 会随着方法结束而变成垃圾,因此,大部分时候程序无需将局部,引用变量显式设为null。

例如:

public void test() {
Object obj = new Object();
//……
obj = null;
}

上面这个就没必要了,随着方法test()的执行完成,程序中obj引用变量的作用域就结束了。但是如果是改成下面:

public void test() {
Object obj = new Object();
//……
obj = null;
//执行耗时,耗内存操作;或调用耗时,耗内存的方法
//……
}

这时候就有必要将obj赋值为null,可以尽早的释放对Object对象的引用。

18.尽量避免使用二维数组

二维数据占用的内存空间比一维数组多得多,大概10倍以上。

19.尽量避免使用split

除 非是必须的,否则应该避免使用split,split由于支持正则表达式,所以效率比较低,如果是频繁的几十,几百万的调用将会耗费大量资源,如果确实需 要频繁的调用split,可以考虑使用apache的StringUtils.split(string,char),频繁split的可以缓存结果。

20.ArrayList & LinkedList

一 个是线性表,一个是链表,一句话,随机查询尽量使用ArrayList,ArrayList优于LinkedList,LinkedList还要移动指 针,添加删除的操作LinkedList优于ArrayList,ArrayList还要移动数据,不过这是理论性分析,事实未必如此,重要的是理解好2 者得数据结构,对症下药。

21.尽量使用System.arraycopy ()代替通过来循环复制数组

System.arraycopy() 要比通过循环来复制数组快的多

22.尽量缓存经常使用的对象

尽可能将经常使用的对象进行缓存,可以使用数组,或HashMap的容器来进行缓存,但这种方式可能导致系统占用过多的缓存,性能下降,推荐可以使用一些第三方的开源工具,如EhCache,Oscache进行缓存,他们基本都实现了FIFO/FLU等缓存算法。

23.尽量避免非常大的内存分配

有时候问题不是由当时的堆状态造成的,而是因为分配失败造成的。分配的内存块都必须是连续的,而随着堆越来越满,找到较大的连续块越来越困难。

24.慎用异常

当创建一个异常时,需要收集一个栈跟踪(stack track),这个栈跟踪用于描述异常是在何处创建的。构建这些栈跟踪时需要为运行时栈做一份快照,正是这一部分开销很大。当需要创建一个 Exception 时,JVM 不得不说:先别动,我想就您现在的样子存一份快照,所以暂时停止入栈和出栈操作。栈跟踪不只包含运行时栈中的一两个元素,而是包含这个栈中的每一个元素。

如果您创建一个 Exception ,就得付出代价。好在捕获异常开销不大,因此可以使用 try catch 将核心内容包起来。从技术上讲,您甚至可以随意地抛出异常,而不用花费很大的代价。招致性能损失的并不是 throw 操作——尽管在没有预先创建异常的情况下就抛出异常是有点不寻常。真正要花代价的是创建异常。幸运的是,好的编程习惯已教会我们,不应该不管三七二十一就 抛出异常。异常是为异常的情况而设计的,使用时也应该牢记这一原则。

原文链接:java编程中'为了性能'一些尽量做到的地方

]]>
Mon, 10 Sep 2012 06:59:27 +0000 viLuo http://viluo.com/post/46
我对创业和管理的一些看法 http://viluo.com/post/45 创业,对于刚工作的人,会比较兴奋,因为创业充满想象力;对于工作几年的人,会比较向往,因为压抑得太久。其实,创业和就业一样,只是实现自己人生价值的两种方式,关键是心态问题。大多数就业的人,一般都把工作叫打工,这是一种对自己不太负责人的态度,其深层次的原因,是因为个人还没有完全独立:对自己的选择负起责任。因为没有谁要求你必须很乏味地工作,没有人要求你必须朝九晚五,你完全可以选择自己的生活,甚至住在山林里。虽然我们可以找出很多被迫的理由,但归根结底,还是自己不能放弃,比如诱人的薪水、优越的身份。

谈到创业,就会谈到失败。但如何来定义呢?也许人生本来就没有成败,生命本身就是一种过程。我对成功的唯一定义,就是做自己想做的事情,实现自己设定的目标。它只决定于你自己,自己的心态,而不是和他人争输赢。

可能有人会觉得这是一种很浅薄、无奈的庸人心态。也许是因为曾经12年的疾病梦魇(主要还是心理上),伴随我从初二到研三毕业。我对生活有一个底线,那就是只要不得癌症,一切都可以重来。因为这种心态,在别人眼中的工作、经济压力,我看的谈很多,自然也过得洒脱些。

说到心态,就从价值观谈起。

价值观

我认为合作的最理想状态,就是默契,而默契最核心的,就是价值观一致。虽然说核心成员最好是专业互补,但这更多是技术层面,价值观比专业技能更基础,更决定创业成败。你觉得你的人生幸福,是决定于你的价值观还是你的职业?没有默契的团队,沟通效率、执行力都大打折扣,很难形成信任的合作关系,成大事的几率也很低。

如果你认为创业就是发家致富,那么估计去人才市场招人,和到电脑城买电脑没啥那样,经济实惠。如果你在心里就认为别人是给你打工,你怎么能够期待别人以创业的心态给你做事呢?

做生意和做企业是两种完全不同的态度,会在企业管理的各种行为中体现出来。我们做每一个决策的背后,几乎都有价值观的支撑。

打个比方,如果做一个电子商务网站,是成都市二环内,还是成都市区一律免费送货上门;退换货是7天还是60天,是否无条件。这不只是一个策略问题,而是对生意和服务的理解。当然,你可能会说,企业生存不下去,还谈什么服务。

公司的价值观,就是老板的价值观,无论是大企业还是小公司,就像一个人在成年期和儿童期,性格不会有太大差别。当公司不断发展,老板会不断引进和他价值观类似的人,那些相处不来的,是不可能长久呆下去的,干得不爽、也很难被重用(重用决定于被信任)。

这也暗含了一个信息,资深人士求职时,最好了解一下公司创始人的人品和做事方式。生意人背景发家的公司,进里面的研发部门最好掂量一下(用生意的思维来做研发管理)。

时势

我理解的时势,就是时机+趋势。时势本质上属于环境的范畴,人都是生活在环境中。个人的聪明和勤奋在时势面前不堪一击。不过你可能会说,你这么聪明和勤奋,怎么会抓不住时势呢?我反驳一下:02年你能预测03年的非典吗? 再比如,02年前后做电子商务是很难成功的,不光是个人PC价格、带宽导致的上网普及,最核心是没有物流、在线支付做支撑。

成功者,一定是有时势做支撑。大家应该都知道06、07年炒股基本上只赚不赔吧?很多人还以为自己是炒股高手呢。

业务需求

也许是在IT行业,总是听别人说想创业。我觉得IT人士是非常不适合创业的,如果一位学会计的说要创业,你是不是觉得有点离谱?IT和会计一样,都是一种工具型、横向职业(什么公司都需要),它是实现业务目标的手段,也就是说,需要业务做支撑,业务才是其核心价值。当然了,也有很多做行业软件的IT公司,但业务精通程度、业务抽象能力,绝对是生存的根基。

比如,即使是一个纯沟通工具,如在线咨询,也是应该深入行业的,比如大的旅游企业,是肯定不会用在线咨询的,因为旅游的个性化、复杂性,在线咨询人工成本非常高(如果平均20分钟的沟通时间,你要养多少客服?)。

再比如,大型电子商务网站的首页一般喜欢用一个flash自动翻页的促销广告,但如果你只是做一次性客人,如张家界旅游网,这种翻页广告就很难有效果,因为大型网站很多是重复性购买客人,他们来看看是否有自己喜欢的,属于被动型消费;而像张家界旅游网的客人目的性很强,直奔主题,是主动消费,不会有耐心看完你的翻页广告。像会员系统、购物车、积分、团购等功能设计都有陷阱,我们往往不客观评估自己网站的位置、客人来自己网站的期望值,而盲目模仿行业标杆。

我们肯定是为客人提供了有价值的产品或服务,客人才会买单。而这种价值,就是满足其需求。

在这儿,我不得不提到的,就是用户体验。用户体验一般人理解为网站界面设计的范畴,这是比较粗浅的。比如电子商务网站,最核心的用户体验是送货速度快、支付便捷、价格便宜等。用户体验=用户关注的价值。

权衡

我觉得,这应该是创业成败的一个决定性因素之一。我们所做的任何决策,都会有正/负两面。比如家里买张摇椅,你考虑到它的享受性,一定要考虑到它还会占空间,特别是小户型。公司强制性加班,一定会对团队凝聚力和员工热情有负面影响。

创业一般是资源受限下的行为,如果资源有限,就一定会考虑如何将有限的资源最大地发挥价值,也就是时间管理上说的轻重/缓急。轻重,即是否为核心需求;缓急,即优先级、顺序。

我记得自己工作那几年,从来不考虑这种问题,领导让做啥就做啥,被动式积极(有任务就全力以赴,没任务就自学、不闻不问),那时候我只是一位执行者。

其实,任何事情都可以分成两阶段:先分配,再执行(日常生活中,我们做任何事情都是先在脑子里分配好了)。而在公司,这两件事往往是分离的:领导做分配,下属做执行。分配任务的核心原则,就是先分清轻重缓急,作为管理者,一定要将它养成习惯。

比如,一个电子商务网站初期上线,你可以将用户点评、后台订单变更等需求放在后面,因为上线第一步是争取订单,而用户点评是用户购买、消费后的行为。这时候,你也不用太关注网站性能/做集群,因为负载还没有影响到用户体验。

再比如,该网站的后台,你可以只开发简易的财务系统,因为这都是人力、时间成本,它在订单量不大时,并不会形成业务处理效率的瓶颈;这时候也不用出统计报表,那至少是几个月后的事情。

判断轻重缓急,是基于对行业、业务、资源的了解深度,我们必须修炼自己的内功。

管理

应该说,这几年我的业余时间大部分花在商业、管理的学习,特别是后者,书都买了上百本。不因为什么,只是兴趣。最终我悟出了三个道理:

1、管理是一种是实践活动,唯一的检验标准就是绩效。适合、有效的管理才是最好的管理,谈管理方法的优劣都是胡扯。

2、创业期精力本身就是稀缺资源,花太多时间学管理很不值。创业初期太了解管理,往往思维会被僵化/教条,而且这个时期,管理不会形成瓶颈。管理本质解决的是秩序。无序才需要被管理,公司只有几个人还谈不上什么混乱。

3、MBA上的管理基本上是对成熟大企业的归纳、总结,对处于婴儿期的创业型企业,基本不具可操作性,比如余世维的《职业经理人》系列。

管理决定于人的做事、做人风格。创业时,你的做事方式肯定还是延续以前的,做人呢,基本从管理书中学不到,至少非常难以转化为行为(比如教练型领导,有多少人能够做好?)。那么学它又有什么用呢?你知道,开车时最怕的就是遇到刚刚拿驾照的新手,不小心就会和你的车kiss一下。如果在创业初期,你能够骑车到达目的地,就不用先花两个月拿驾照,再花三个月熟练吧?

上面我也说到,我花了很多时间了解商业。现在想想,商业是一件很朴素的事情:如何挖掘你的目标客户群的需求,为他们创造实实在在的价值,从而获得回报。知道要满足什么需求,剩下的都是手段,用我们IT行业的术语,就是“技术实现”,技术实现一般是一种比较低级的活吧?

因为这个感悟,看商业杂志,也就理性多了。中国的很多成功人士,或者说有钱人吧,发家一般都是早期抓住了某种用户需求,并不是因为发家初期,管理水平如何。其实,管理一定程度上,也是实现业务需求的手段。

看商业杂志,扩大自己的视野倒是可以,但如果寄希望它找点子,从而开创事业,基本上不靠谱。现在创业,基本上不可能靠一个点子就成功,特别是不熟悉的行业。即使是一个好点子,要几个月不折不扣地执行,也是非常考验人。

那些我们看起来很诱人的行业,水更深,比如SNS、团购,光营销费用都是以百万计,都需要规模优势,1000家做团购的,可能最后生存下来的不到20家,你有这么幸运?这些成功根本不是技术问题。江南春的分众传媒,就是在每家楼宇里面挂上若干电子相框,有技术含量吗?

所以有人说,失败是必然,成功才是偶然。我们看到的机会,往往都是陷阱,但不尝试又怎么知道。当然了,这说的都是大机会。做点电子商务,赚点小钱,门槛倒不高。

奖惩 大多数企业对员工激励的方式,基本上就等同于奖惩了。我估计大多数人对胡萝卜加大棒的管理很适应,那是否也暗示,自己也愿意做那只兔子?没有任何人喜欢被控制,那么,如果你是管理者,你是否还偏好这种对待动物的方法? 我觉得,与其说是奖惩,不如用规则这个词更合适:制定一个能够达成共识的规则或制度,让每个人都在这个规则的平台上展现自己,而不是由某个高高在上的领导给每个人或萝卜或大棒。

管理的最高境界,应该是建立一个企业生态系统。

企业文化 企业文化是最近几年谈得最火的。企业文化就是管理者的文化,是基于企业创始人的价值观。很多企业喜欢鼓吹自己的企业文化,说他们是文化领导,更确切地说,应该是文化控制。我觉得,改变一个人的价值观根本不现实,特别是企业这种盈利单位。不过,我们每个人都认同一些普世原则:尊重、信任、双赢、公平等。与其大费周折让员工接受某人的价值观,还不如去唤醒或强化那种流淌于每个人血液中的人性光辉。

小企业谈不上文化,因为文化需要一种势,一种集体的势(氛围),通过大多数人的行为来影响少数人。

职业化 职业化大概是余世维04年左右,在《职业经理人》中开始普及的,我曾经也是高度颂扬和推行职业化,但无疾而终。职业化在整个中国都处于启蒙阶段,中国是一个关系社会,从这些词就可以看出来,如忠诚、面子。忠诚是一种人身依附,面子是一种人情为上。职业化需要规则的土壤,中国目前不具备,某些行业的着装和举止很职业化,但行为或意识还没有跟上,而且职业化似乎主要针对对基层服务人员,如职业化开始比较早的航空公司、银行、酒店。

创业型公司,谈职业化为时尚早,因为这个阶段,往往是因为领导者的魅力或人品,大家一起干,而不是因为公司公司雇佣我,所以我必须按某某规矩做,靠契约来形成内心动力和责任。

战略 创业型公司,最好不要大谈战略,因为还处于摸索阶级,做战略决策一般是凭感觉/直觉。有过几年的感觉,慢慢会凭经验。也许等到公司足够强大,对行业和本企业有很深的理解后,方可凭方法做事了。MBA里面的战略,一般都是基于方法学,比如SWOT分析、BCG矩阵;对初创公司,往往只有有限的参考价值。

什么叫战略,在企业初期,就是投入钱和人,尽快拿回订单;战略更多是一种策略,而不是定义上的的方向性指导。

企业生命周期

因为对企业生命周期的不敏感(婴儿期-成长期-成熟期-衰退期),估计让很多人走过弯路。就像你在25岁吃六味地黄丸,这种中老年人吃的补药,对身体往往是负面影响的。很多企业领导,因为看到某企业用某管理方法,业绩大增,于是也想试试。比如GE的末位淘汰(每年淘汰10%),你觉得可以采用吗?GE的企业品牌、规模、薪酬待遇均有行业领先性,你具备吗?确实,大多数优秀的公司都具有末位淘汰,比如阿里巴巴,但这也是它们变得优秀后的强势作风。GE的6Sigma,那就更别学了,因为这种质量控制流程和标准你既做不出来,成本也太高。

有句话说,一流的公司卖标准,二流的公司卖品牌,三流的公司卖产品。不是有mp3和mp4格式吗?你来一个mp5格式,看市场要多少年才会接纳你?那些制造标准的公司,一开始也不是以标准出现的,只是自己的方案太普遍,于是就形成标准了,就像曾经席梦思是床垫的代名词。所以,要认清自己所处的阶段后,再考虑做相应事情。一上来就想做平台,一般最后都是一个空壳。

创业需要把握节奏,比如做电子商务网站,一般是先上产品、再上会员系统,再上社区。而我们模仿时,往往三个同时上,最后的结局是,一年下来,论坛只有10个帖子,除了告诉别人你们网站没有人气、不值得信赖,仅此而已。

说到这里,又想到一句创业口号:创新。没有经历模仿和改进阶段,创新是否有支撑?就像我们读书那十几年,其实都是学死知识,也就是模仿,比如学习议论文的三段论。我不是不鼓励创新,只是觉得,创业的成本和风险非常高,时间和资金一般公司撑不起。有兴趣的朋友,可以找找苹果公司那些没有面市的产品模型。

也经常听人说到精细化和粗放型,如果从企业生命周期角度看,初期偏粗放,然后再慢慢精细化运作,比如质量控制流程

企业很小的时候,人少,一般靠信任式管理。信任,本身就是一种约束。这体现在上下班考勤、权限分配、绩效考核等等。

说说绩效考核吧,绩效考核的本意是为了提升绩效(提醒你哪儿需要改进),但实际上,一般都成为发放薪酬和辞退员工的手段(员工一般都这么理解)。这在无形中就暗示了一种雇用关系,助长员工的打工心态,还导致上下级间的隔阂、增强了彼此间的不信任。我觉得小企业对于某些岗位,完全可以放弃绩效考核,特别是研发型团队。

暗示这个词很微妙,就像你不会给你的上司送礼物,而上司可以给你送小礼物,这暗示了一种等级关系:我认可你的成绩(反之,你不会用认可这种词对上司吧)。这谈不上什么办公室政_治,我只是觉得,作为管理者,一定要留意自己行为的侧面影响。影响力,往往不是纸上或口头上,而是在我们的行为中。

流程化,是一个很美妙的词语,因为优秀的大企业一般都是这么做的。流程化带来效率的同时,必然要求分工明确,而分工明确往往就意味着组织要加人,而在企业创立初期加人是一件很艰难的事情。

流程化的制定,本身就是一个漫长的优化过程,而且,不同的业务、人员规模,往往业务流程是不一样的。

为什么说流程的制定是一个漫长的过程呢?就像你开车上班,你要设计出一条最佳线路,也是需要不断尝试的。我的意思是,创业型企业,不要一开始就上严格的流程,等到业务处理出现效率瓶颈时,再一步步优化。渐进式改革,一般都比革_命好。

好了,先写这儿。感悟往往都是一点一滴,所以写得也比较零碎。

最后,推荐几本好书:

《从命令到参与》:人性化,可操作的管理理念和技术。普适的书,虽然是外国人写的。

《西方管理思想史》:了解各种管理模式的起源,会更深懂得管理的适用性,比如泰勒主义。

《中国企业批判》:一位管理咨询师写的,很冷静、深刻,创业者一定要看。国人能够写出这种书,佩服佩服。

《管理咨询的神话》:对管理大师的深刻剖析和批判。还有一本是《企业巫医》,但比较浅薄,有点哗众取宠。

《重塑管理》:很实用、深刻。

《真实情景中的管理学》:可以系统了解管理框架。毕竟叫管理“学”,不是实战型,比起其它几本知名教程,这个有趣得多。

以上推荐的都比较有思想,不会让你对管理形成呆板的认识,就当消遣吧。像《战略管理》这类书,要慎读,高瞻远瞩往会一脚踏空。

原文链接: 我对创业和管理的一些看法

]]>
Mon, 10 Sep 2012 02:59:39 +0000 viLuo http://viluo.com/post/45
编码最佳实践--小心!这只是冰山一角 http://viluo.com/post/44 本期的案例依然是来自实际项目,很寻常的代码,却意外遭遇传说中的Java"内存溢出"。

先来看看发生了什么,代码逻辑很简单,在请求的处理过程中:

1. 创建了一个ArrayList,然后往这个list里面放了一些数据,得到了一个size很大的list

List cdrInfoList = new ArrayList();
for(...) {
cdrInfoList.add(cdrInfo);
} 

2. 从这个list里面,取出一个size很小的sublist(我们忽略这里的业务逻辑)

cdrSublist = cdrInfoList.subList(fromIndex, toIndex)

3. 这个cdrSublist被作为value保存到一个常驻内存的Map中(同样我们忽略这里的业务逻辑)

cache.put(key, cdrSublist);

4. 请求处理结果,原有的list和其他数据被抛弃

正常情况下保存到cdrSublist不是太多,其内存消耗应该很小,但是实际上sig的同事们在用JMAP工具检查SIG的内存时,却发现这 里的subList()方法生成的RandomAccessSubList占用的内存高达1.6G! 完全不合符常理。

我们来细看subList()和RandomAccessSubList在这里都干了些什么:详细的代码实现追踪过程请见附录1,我们来看关键代码,类SubList的实现代码,忽略不相关的内容

这里我们可以清楚的看到SubList的实现原理:

1. 保存一个原始list对象的引用

class SubList<E> extends AbstractList<E> {
private AbstractList<E> l;
private int offset;
private int size;
SubList(AbstractList<E> list, int fromIndex, int toIndex) {
......
l = list;
offset = fromIndex;
size = toIndex - fromIndex;
}
}

2. 用offset和size来表明当前sublist的在原始list中的范围

为了让大家有一个感性的认识,我们用debug模式跑了一下测试代码,截图如下:

点击查看原图

可以看到生成的sublist对象内有一个名为"l"的属性,这是一个ArrayList对象,注意它的id和原有的list对象相同(图中都是id=33)。

这种实现方式主要是考虑运行时性能,可以比较一下普通的sublist实现:

public List<E> subList(int fromIndex, int toIndex) {
List<E> result = ...; // new a empty list
for (int i = fromIndex; i <= toIndex; i++) {
result.add(this.get(i));
}
return result;
}

这种实现需要创建新的list对象,然后添加所需内容,相比之下无论是内存消耗还是运行效率都不如前面SubList直接引用原始 list+记录偏差量的方式。

但是SubList的这种方式,会有一个极大的隐患:这个SubList的实例中,保存有原有list对象的引用——而且是强引用,这意味着, 只要sublist没有被jvm回收,那么这个原有list对象就不能gc,这个list中保存的所有对象也不能gc,即使这个list和其包含的对象已经没有其他任何引用。

这个就是Java世界中“内存泄露"的一个经典实例:某些被期望能被JVM回收的对象,却因为某个没有被觉察到的角落中"偷偷的"保留 了一个引用而躲过GC......在SIG的这个例子中,我们本来只想在内存中保留很少很少的一点点数据,被意外的将整个list和它包含的所 有对象都留下来。注意在截图中,list的size为100000,而sublist只是1而已,这就是我们标题中所说的"冰山一角"。

这里有一段实例代码,大家可以运行一下,很快就可以看到Java世界中名声显赫的OOM:

public class SublistTest {
public static void main(String[] args) {
List<List<Integer>> cache = new ArrayList<List<Integer>>();
try {
while (true) {
List<Integer> list = new ArrayList<Integer>();
for (int j = 0; j < 100000; j++) {
list.add(j);
}
List<Integer> sublist = list.subList(0, 1);
cache.add(sublist);
}
} finally {
System.out.println("cache size = " + cache.size());
}
}
} 

在我的测试中,打印结果为"cache size = 121",也就是说我的测试中121个list,每个list里面只放了一个Integer对象,就可以吃 掉所有内存,造成out of memory.

仔细的同学会发现,其实在sublist()方法的javadoc里面,已经对此有明确的说明,“The returned list is backed by this list” ,因此提醒大家在使用某个不熟悉的方法之前最好读一读Javadoc:

Returns a view of the portion of this list between fromIndex, inclusive, and toIndex, exclusive. (If fromIndex and toIndex are equal, the returned list is empty.) The returned list is backed by this list, so changes in the returned list are reflected in this list, and vice-versa. The returned list supports all of the optional list operations supported by this list.

同样的,在java中还有一个非常类似的案例,来自最常见的String类,它的substring()方法和split()方法,大家可以翻开jdk 的源码看到具体代码。原理和sublist()方法非常类似,就不重复解释了。

简单给出一段代码,演示一下substring()方法在类似情景下是如何OOM的:

public class SubstringTest {
public static void main(String[] args) {
List<String> cache = new ArrayList<String>();
try {
int i = 1;
while (true) {
String original = buildABigString(i++);
String substring = original.substring(0, 1);
cache.add(substring);
}
} finally {
System.out.println("cache size = " + cache.size());
}
}
private static String buildABigString(int count) {
long thistime = System.currentTimeMillis() + count;
StringBuilder buf = new StringBuilder(1024 * 100);
for (int i = 0; i < 10000; i++) {
buf.append(thistime);
}
return buf.toString();
}
}

这一次,我的测试用只用了994个长度为1的字符串,就"成功"达到了OOM。

最后谈一下怎么解决上面的问题,当然前提是我们有需要将得到的小的list或者string长时间存放在内存中:

1. 对于sublist()方法得到的list,貌似没有太好的办法,只能用最直接的方式:自己创建新的list,然后将需要的内容添加进去

2. 对于substring()/split()方法得到的string,可以用String类的构造函数new String(String original)来创建一个新的String,这 样会重新创建底层的char[]并复制需要的内容,不会造成"浪费"。

String类的构造函数new String(String original)是一个非常特别的构造函数,通常没有必要使用,正如这个函数的javadoc所言 :Unless an explicit copy of original is needed, use of this constructor is unnecessary since Strings are immutable. 除非明确需要原始字符串的拷贝,否则没有必要使用这个构造函数,因为String是不可变的。

但是对于前面的这种特殊场景(从超大字符串中substring()得到后再放置到常驻内存的结构中),new String(String original)就 可以将我们从这种潜在的内存溢出(或者浪费)中拯救出来。因此,当遇到同时处理大字符串+长时间放置内容在内存中时,请小心。

最后鸣谢Ray Tao同学为本次分享提供素材!

附录:List.sublist() 代码实现追踪

1. ArrayList的代码,继承自AbstractList,实现了RandomAccess接口

public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable 

2. AbstractList类的subList()函数的代码,对于ArrayList,返回RandomAccessSubList的实例

public List<E> subList(int fromIndex, int toIndex) {
return (this instanceof RandomAccess ? new RandomAccessSubList<E>(this, fromIndex, toIndex)
: new SubList<E>(this, fromIndex, toIndex));
}

3. RandomAccessSubList的代码,继承自SubList

class RandomAccessSubList<E> extends SubList<E> implements RandomAccess {
RandomAccessSubList(AbstractList<E> list, int fromIndex, int toIndex) {
super(list, fromIndex, toIndex);
}
public List<E> subList(int fromIndex, int toIndex) {
return new RandomAccessSubList<E>(this, fromIndex, toIndex);
}
}

原文链接:编码最佳实践(5)--小心!这只是冰山一角

]]>
Mon, 10 Sep 2012 02:24:19 +0000 viLuo http://viluo.com/post/44
编码最佳实践--小心LinkedHashMap的get()方法 http://viluo.com/post/43 这是一个来自实际项目的例子,在这个案例中,有同事基于jdk中的LinkedHashMap设计了一个LRUCache,为了提高性能,使用了 ReentrantReadWriteLock 读写锁:写锁对应put()方法,而读锁对应get()方法,期望通过读写锁来实现并发get()。

代码实现如下:

private ReentrantReadWriteLock  lock = new ReentrantReadWriteLock ();
lruMap = new LinkedHashMap<K, V>(initialCapacity, loadFactor, true)
public V get(K key) {
lock.readLock().lock();
try {
return lruMap.get(key);
} finally {
lock.readLock().unlock();
}
}
public int entries() {
lock.readLock().lock();
try {
return lruMap.size();
} finally {
lock.readLock().unlock();
}
}
public void put(K key, V value) {
...
lock.writeLock().lock();
try {
...
lruMap.put(key, value);
...
} finally {
lock.writeLock().unlock();
}
}

在测试中发现问题,跑了几个小时系统就会hung up,无法接收http请求。在将把线程栈打印出来检查后,发现很多http的线程都在等读锁。有一个 runnable的线程hold了写锁,但一直停在LinkedHashMap.transfer方法里。线程栈信息如下:

"http-0.0.0.0-8081-178" daemon prio=3 tid=0x0000000004673000 nid=0x135 waiting on condition [0xfffffd7f5759c000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for  <0xfffffd7f7cc86928> (a java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:156)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:811)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireShared(AbstractQueuedSynchronizer.java:941)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireShared(AbstractQueuedSynchronizer.java:1261)
at java.util.concurrent.locks.ReentrantReadWriteLock$ReadLock.lock(ReentrantReadWriteLock.java:594)
......
"http-0.0.0.0-8081-210" daemon prio=3 tid=0x0000000001422800 nid=0x155 runnable [0xfffffd7f5557c000]
java.lang.Thread.State: RUNNABLE
at java.util.LinkedHashMap.transfer(LinkedHashMap.java:234)
at java.util.HashMap.resize(HashMap.java:463)
at java.util.LinkedHashMap.addEntry(LinkedHashMap.java:414)
at java.util.HashMap.put(HashMap.java:385)
...... 

大家都知道HashMap不是线程安全的,因此如果HashMap在多线程并发下,需要加互斥锁,如果put()不加锁,就很容易破坏内部链表,造成get()死循 环,一直hung住。这里有一个来自淘宝的例子,有对此现象的详细分析:https://gist.github.com/1081908

但是在MSDP的这个例子中,由于ReentrantReadWriteLock 读写锁的存在,put()和get()方法是互斥,不会有上述读写竞争的问题。

Google后发现这是个普遍存在的问题,其根结在于LinkedHashMap的get()方法会改变数据链表。我们来看一下LinkedHashMap的实现代码:

public V get(Object key) {
Entry<K,V> e = (Entry<K,V>)getEntry(key);
if (e == null)
return null;
e.recordAccess(this);
return e.value;
}
void recordAccess(HashMap<K,V> m) {
LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
if (lm.accessOrder) {
lm.modCount++;
remove();
addBefore(lm.header);
}
}
void transfer(HashMap.Entry[] newTable) {
int newCapacity = newTable.length;
for (Entry<K,V> e = header.after; e != header; e = e.after) {
int index = indexFor(e.hash, newCapacity);
e.next = newTable[index];
newTable[index] = e;
}
}

前面LRUCache的代码中,是这样初始化LinkedHashMap的:

lruMap = new LinkedHashMap<K, V>(initialCapacity, loadFactor, true)

LinkedHashMap构造函数中的参数true表明LinkedHashMap按照访问的次序来排序。这里所谓的按照访问的次序来排序的含义是:当调用LinkedHashMap 的get(key)或者put(key, value)时,如果key在map中被包含,那么LinkedHashMap会将key对象的entry放在线性结构的最后。正是因为LinkedHashMap提 供按照访问的次序来排序的功能,所以它才需要改写HashMap的get(key)方法(HashMap不需要排序)和HashMap.Entry的recordAccess(HashMap)方法。注 意addBefore(lm.header)是将该entry放在header线性表的最后。(参考LinkedHashMap.Entry extends HashMap.Entry 比起HashMap.Entry多了before, after两个域,是双向的)

在上面的LRUCache中,为了提供性能,通过使用ReentrantReadWriteLock读写锁实现了并发get(),结果导致了并发问题。解决问题的方式很简单, 去掉读写锁,让put()/get()都使用普通互斥锁就可以了。当然,这样get()方法就无法实现并发读了,对性能有所影响。

总结,在使用LinkedHashMap时,请小心LinkedHashMap的get()方法。

原文链接:编码最佳实践(4)--小心LinkedHashMap的get()方法

]]>
Thu, 06 Sep 2012 11:17:46 +0000 viLuo http://viluo.com/post/43
编码最佳实践--尽量重用昂贵的初始化对象 http://viluo.com/post/42 这里将要讲述的是一系列的类似案例,都是在各个产品进行performance tuning时被发现的,非常具有普适性。可以说在日常开发中,有非常大的概率遇到相同或者类似的情形,因此需要对其保持警惕以便避免陷入类似的性能问题。

我们从JAXBContext这个对象开始,JAXBContext 是JAXB API的入口,典型的代码实现如下:

private void unmarshal() {
JAXBContext context = JAXBContext.newInstance(DirectoryConstants.JAXB_CONTEXT_CLASS);
Unmarshaller u = context.createUnmarshaller();
Object obj = u.unmarshal(...);
}

这个是标准使用流程了,首先初始化JAXBContext对象,通过JAXBContext对象创建Unmarshaller对象,然后使用Unmarshaller对象来进行unmarshal操作。

这个写法在功能实现上没有任何问题,但是如果一旦进行压力测试,就会暴露性能问题。JAXBContext对象的初始化是个资源消耗非常大的操作,我们可以通过threaddump进行分析,会发现很多工作线程都在执行JAXBContext.newInstance()这个方法,而不是我们期待的u.unmarshal()。而事实上JAXBContext对象的Sun实现是线程安全的,即容许多线程同时调用一个JAXBContext对象的createUnmarshaller(),因此完全没有必要为每个xml的 marshaling 和 unmarshalling 操作去初始化一次JAXBContext对象。可以参考这里的说明:https://jaxb.dev.java.net/faq/index.html#threadSafety

简单点说,这个一个初始化代价昂贵,却又可重复使用的对象。

因此,我们只需要初始化JAXBContext对象一次并保存起来,然后重复使用这个JAXBContext对象即可。保存JAXBContext对象的方式可以有很多种,比如cache / threadlocal,或者使用一个单例来维持这个对象。比如下面的代码示例:

private void unmarshal() {
JAXBContext context = JAXBContextHolder.get();
...
}
public class JAXBContextHolder {
private static final JAXBContext instance = JAXBContext.newInstance(DirectoryConstants.JAXB_CONTEXT_CLASS);
public static JAXBContext get() {
return instance;
}
}

在实际项目中, 通过上面的简单改进之后,我们当时得到了一个非常巨大的回报:TPS (transactions per second 每秒事务处理量)直接*3 !!

这个案例从技术上讲非常的简单,道理很浅显,相信每个人都能轻松理解。或者说,这是一个“知道了就简单,不知道就容易犯错而不自知”的地方,因此依然有些东西需要注意:

(1) 有哪些对象有类似的特性

目前发现的类似对象有

1. 刚刚上面讲到的JAXB API中的 JAXBContext 对象

2. SOAP API中的javax.xml.soap.SOAPFactory

3. CFX client

通常情况下,在使用各种api或者工具类库时,如果发现调用代码中有类似的初始化语句,都应该稍加注意(除非明确当前代码对性能完全没有要求),可以去查一下这个类对象的javdoc或者直接看源码,如果发现满足上面所说的特性,则应该考虑进行上述的性能优化。

根据经验,类似的初始化对象通常的命名规则都是***Context/***Factory之类,或者***client,遇到类似名字时需要提高警惕。

(2)假设问题已经存在,如果才能在performance tuning中迅速发现问题的代码?

通常的办法就是用thread dump,一般连续dump个3-5次,然后通过分析thread dump信息 (推荐使用eclipse插件 lockness),看当前请求的线程(通常是一个线程池)都在干什么。一般初始化昂贵都昂贵在类似文件IO操作或者加锁之类的地方,很容易在thread dump中被发现。

原文链接:编码最佳实践(3)--尽量重用昂贵的初始化对象

]]>
Thu, 06 Sep 2012 10:56:46 +0000 viLuo http://viluo.com/post/42
编码最佳实践--推荐使用concurrent包中的Atomic类 http://viluo.com/post/41 这是一个真实案例,曾经惹出硕大风波,故事的起因却很简单,就是需要实现一个简单的计数器,每次取值然后加1,于是就有了下面这段代码:

private int counter = 0;
public int getCount(){
return counter++;
}

这个计数器被用于生成一个sessionId,这个sessionID用于和外部计费系统交互,这个sessionId理所当然的要求保证全局唯一而不重复。但是很遗憾,上面的代码最终被发现会产生相同的id,因此会造成一些请求莫名其妙的报错.....更痛苦的是,上面这段代码是一个来自其他部门开发的工具类,我们当时只是拿了它的jar包来调用,没有源码,更没有想这里面会有如此低级而可怕的错误。

由于重复的sessionId,造成有个别请求失败,虽然出现概率极低,经常跑一天测试都不见得能重现一次。因为是和计费相关,因此哪怕是再低的概率出错,也不得不要求解决。实际情况是,项目开发到最后阶段,都开始做发布前最后的稳定性测试了,在7*24小时的连续测试中,这个问题往往在测试开始几天后才重现,将当时负责trouble shooting的同事折腾的很惨......经过反复的查找,终于有人怀疑到这里,反编译了那个jar包,才看到上面这段出问题的代码。

这个低级的错误,源于一个java的基本知识:

++操作,无论是i++还是++i,都不是原子操作!

而一个非原子操作,在多线程并发下会有线程安全的问题:这里稍微解释一下,上面的"++"操作符,从原理上讲它其实包含以下:计算加1之后的新值,然后将这个新值赋值给原变量,返回原值。类似于下面的代码

private int counter = 0;
public int getCount() {
int result = counter;
int newValue = counter + 1; // 1. 计算新值
counter = newValue; // 2. 将新值赋值给原变量
return result;
}

多线程并发时,如果两个线程同时调用getCount()方法,则他们可能得到相同的counter值。为了保证安全,一个最简单的方法就是在getCount()方法上做同步:

private int counter = 0;
public synchronized int getCount() {
return counter++;
}

这样就可以避免因++操作符的非原子性而造成的并发危险。

我们在这个案例基础上稍微再扩展一下,如果这里的操作是原子操作,就可以不用同步而安全的并发访问吗?我们将这个代码稍作修改:

private int something = 0;
public int getSomething() {
return something;
}
public void setSomething(int something) {
this.something = something;
}

假设有多线程同时并发访问getSomething()和setSomething()方法,那么当一个线程通过调用setSomething()方法设置一个新的值时,其他调用getSomething()的方法是不是立即可以读到这个新值呢?这里的"this.something = something;" 是一个对int 类型的赋值,按照java 语言规范,对int的赋值是原子操作,这里不存在上面案例中的非原子操作的隐患。

但是这里还是有一个重要问题,称为"内存可见性"。这里涉及到java内存模型的一系列知识,限于篇幅,不详尽讲述,不清楚这些知识点的可以自己翻翻资料,最简单的办法就是google一下这两个关键词"java 内存模型", "java 内存可见性"。或者,可以参考这个帖子"java线程安全总结", http://www.iteye.com/topic/806990。

解决这里的"内存可见性"问题的方式有两个,一个是继续使用 synchronized 关键字,代码如下

private int something = 0;
public synchronized int getSomething() {
return something;
}
public synchronized void setSomething(int something) {
this.something = something;
}

另一个是使用volatile 关键字,

private volatile int something = 0;
public int getSomething() {
return something;
}
public void setSomething(int something) {
this.something = something;
}

使用volatile 关键字的方案,在性能上要好很多,因为volatile是一个轻量级的同步,只能保证多线程的内存可见性,不能保证多线程的执行有序性。因此开销远比synchronized要小。

让我们再回到开始的案例,因为我们采用直接在 getCount() 方法前加synchronized 的修改方式,因此不仅仅避免了非原子性操作带来的多线程的执行有序性问题,也"顺带"解决了内存可见性问题。

OK,现在可以继续了,前面讲到可以通过在 getCount() 方法前加synchronized 的方式来解决问题,但是其实还有更方便的方式,可以使用jdk 5.0之后引入的concurrent包中提供的原子类,java.util.concurrent.atomic.Atomic***,如AtomicInteger,AtomicLong等。

private AtomicInteger counter = new AtomicInteger(0);
public int getCount() {
return counter.incrementAndGet();
}

Atomic类不仅仅提供了对数据操作的线程安全保证,而且提供了一系列的语义清晰的方法如incrementAndGet(),getAndIncrement,addAndGet(),getAndAdd(),使用方便。更重要的是,Atomic类不是一个简单的同步封装,其内部实现不是简单的使用synchronized,而是一个更为高效的方式CAS (compare and swap) + volatile,从而避免了synchronized的高开销,执行效率大为提升。限于篇幅,关于“CAS”原理就不在这里讲诉。

因此,出于性能考虑,强烈建议尽量使用Atomic类,而不要去写基于synchronized关键字的代码实现。

最后总结一下,在这个帖子中我们讲诉了一下几个问题:

1. ++操作不是原子操作

2. 非原子操作有线程安全问题

3. 并发下的内存可见性

4. Atomic类通过CAS + volatile可以比synchronized做的更高效,推荐使用

原文链接:编码最佳实践(2)--推荐使用concurrent包中的Atomic类

]]>
Thu, 06 Sep 2012 10:24:02 +0000 viLuo http://viluo.com/post/41
编码最佳实践--小心"数据溢出" http://viluo.com/post/40 最近在公司内部做了一些收集和整理的工作,关于trouble shooting和performace tuning 中遇到并解决的典型问题,做了一些内部分享。我整理了一下,准备陆续放上来分享给大家。

这些问题,单个看每个问题都不算复杂或高深,但是都是在实际项目开发中出现并一度造成困扰的,而且带有一定的普适性,具体表现为不知道这些问题的同学很容易在日常开发中中招。因此我们开了一个专题,叫做编码最佳实践,似乎名字起的有点大......

先来看看第一个,如何做compare。

先看案例,问题的表现很简单,就是在排序后的结果中有时会很惊讶的发现排序错误。我们不纠结于具体的错误表现细节和排查的过程,直接来看最终被检查出问题所在的代码,这是一个很普通的Comparator接口实现:

private static class keyOrderComparator implements Comparator<Persistent> {
public int compare(Persistent p1, Persistent p2) {
return (int) (p1.getId().getKey() - p2.getId().getKey());
}
}

代码中的比较逻辑很简单,比较Persistent对象的id的key值就OK,实现中将两个key简单做一次减法运算,将结果作为compare()方法的返回值。如果p1的key大于 p2的key,则"p1.getId().getKey() - p2.getId().getKey()"的结果大于0,而compareTo()方法返回一个大于0的整数表示比较结果为"参数p1大于参数p2"。

但麻烦出现在key的数据类型上,这是一个long类型,因此减法运算的结果也是一个long,为了满足compare()方法要求返回int的要求,在return前做了一次强制类型转换。而问题就出现在这里:从long到int的强制类型转换是有风险的,如果long的数字超过了int所能表示的范围[Integer.Min_VALUE, Integer.Max_VALUE],则会发生"数据溢出"(data overflow)。

我们可以试着执行以下代码 System.out.println((int) (30000000000L - 1)); , 会发现它的结果是一个"-64771073",和意想中的29999999999完全不同,重要的是符号变了:从一个正数变成了负数!这直接导致了compare()方法得出了一个令人惊讶的比较结果:30000000000 比 1 小!

解决方式也很简单,不要做强制类型转换:

private static class keyOrderComparator implements Comparator<Persistent> {
public int compare(Persistent p1, Persistent p2) {
long key1 = p1.getId().getKey();
long key2 = p2.getId().getKey();
if (key1 == key2) {
return 0;
} else {
return key1 > key2 ? 1 : -1;
}
}
}

在这个简单案例当中,有一个比较明显的地方可以帮助我们发现问题,就是(int)这个强制类型转换,稍有经验的同学就会第一时间反应过来:long到int是有数据溢出风险的。那如果我们将这个案例稍微修改一下,假设p1.getId().getKey()返回的就是普通的int,结果会如何:

private static class keyOrderComparator implements Comparator<Persistent> {
public int compare(Persistent p1, Persistent p2) {
return p1.getId().getKey() - p2.getId().getKey();
}
}

这段代码貌似就没有问题啦?呵呵,让我们把这段代码的业务含义去掉,退化为一个普通的int比较:

private static class IntegerOrderComparator implements Comparator<Integer> {
public int compare(Integer p1, Integer p2) {
return p1 - p2;
}
}

这下应该能看出来了吧?如果p1=2147483647即Integer.MAX_VALUE,而p2=-1,则p1 - p2 = Integer.MAX_VALUE - (-1) = -2147483648 ! IntegerOrderComparator 会给出一个令人目瞪口呆的比较结果:2147483647 比 -1 小!类似的,在 p1= -2147483648 (Integer.MIN_VALUE), p2 = 1时,IntegerOrderComparator 同样会给出类似荒唐的比较结果:-2147483648 比 1 大!

导致错误发生的原因依然是"数据溢出"!和前面long到int的强制类型转换不同,这次数据溢出发生在int与int之间做数学运算。

我们来看问题发生在哪里:"int - int"这样的简单的运算,在我们的数学常识中,两个整型相减结果肯定还是整型,一个正数减一个负数结果肯定是正数,一个负数减一个正数结果肯定是负数......但是这里的数学常识中所谓的"整型",其取值范围可以是无穷小到无穷大,而java语言(其他语言也是类似)中的int,只能表示[Integer.Min_VALUE, Integer.Max_VALUE],即[-2147483648, 2147483647]这样一个范围。一旦运算的结果超过这个范围,就会发生数据溢出。

因此,在java中,类似"int + int", "int - int", "int * int" 这样的运算结果,用int来表示是不安全的,需要使用更大的数据类型比如long来。上面的代码可以修订为:

private static class IntegerOrderComparator implements Comparator<Integer> {
public int compare(Integer p1, Integer p2) {
long diff = p1 - p2;
return diff == 0 ? 0 : (diff > 0 : 1 : -1);
}
}

但是这种compare的写法,遇到数据范围更大的数据类型时依然有麻烦,因为总是要找到一个比它数据范围还要大的数据类型来承载这个diff的结果。因此还是推荐使用前面的比较方法:不做减法,直接做等于和大于/小于的比较。

最后总结一下这个案例:

1. compare方法实现时,尽量不要用"return p1 - p2"这种写法

2. 但凡进行数值运算时,都要小心考虑数据溢出的风险

3. 做trouble shooting时,要留意可能的数据溢出

PS: 有没有犯同样错误而不自知的同学?请自觉的留个爪子,呵呵

原文链接:编码最佳实践(1)--小心"数据溢出"

]]>
Thu, 06 Sep 2012 10:11:33 +0000 viLuo http://viluo.com/post/40
关于对3A(CNAAA)网络供应商的吐槽 http://viluo.com/post/39 记得以前看过中央电视台法制节目的一款节目,讲到了一个案例:

貌似在北京,有一对小年轻结婚了。男方是北京人,家里也比较有钱,在北京寸土寸金的地方买了房。婚后日子过得很恩爱,日子也过得很美满,妻子也怀孕了。家里都在等着小生命的诞生。

不幸的是,丈夫出了车祸去世了,一家人都很悲痛。但是日子还是要继续过下去的。

问题来了,妻子还怀着孩子,但是丈夫这么早过世,之后肯定还是要改嫁的。公公婆婆失去了唯一的儿子,痛不欲生的同时,就指望着媳妇把孩子生下来,那个小生命也是他们生命里唯一的指望了。于是,公公就和媳妇谈,大体上的意思就是,让媳妇把孩子生下来,然后孩子由他们俩带,媳妇她到时可以去过自己的日子,孩子生下来后,公公会把房子给媳妇(之前房产在公公名下),同时再给她一笔钱。虽然里面涉及到金钱等等,但是于情于理,公公的要求也比较合理。毕竟肚里的孩子也是小两口恩爱的结晶,不为老人想,也该想想去世的丈夫,好得在世上给他留个存在的印记。

媳妇也答应了,公公提前把房子转给了媳妇。

之后的事情就比较挫了,也许是后悔了,不想再婚的时候被打上有孩子的印记,也许是一些其他我们猜不出的想法。媳妇回了娘家一趟后,把孩子打掉了。然后也把北京的房子卖掉了。

之后公公气不过,打官司把房子要了回来。然后,估计,老死不相往来了。

讲了这么长,想说的其实是,很多事情如果平稳的发展,那么一切都OK,但是一出点事情来,就看出来人心了。

用3A(CNAAA)的主机也用了半年多,之前一直在浙江机房,合作的都蛮不错的,没出过什么大问题。偶尔小当机都在可控范围内。然后7月中旬又买了款虚拟主机。

接下来的事情就坑爹了,用了不到两星期,3A网络通知我们说为了更稳定的服务,机房要搬迁上海。好吧,那就搬吧。然后,悲剧了,突然某一天网站打不开了,一看ip都ping不通。问客服,客服说是有不和谐的内容,导致整个ip被封。受到波及了,好吧,谁让我们用的是虚拟主机呢,没办法。过了好多天,好不容易换了ip,切了过去。然后,又悲剧了,网站三天两头的抽风。哦,说错了,是每天都要抽很长一段时间风。然后又过了几天,突然通知我们说,上海机房有问题,紧急帮我们把网站搬迁到安徽稳定的机房了。

但是你们还可信么?但是搬到上海的时候就是说为了更稳定的环境的啊。

然后说给我们赔偿,赔偿一款299的虚拟空间,好吧,299的配置竟然远不如一款278的配置高,真当我们是傻子么?让换成278的主机也不行,让折成现在空间的续期也不行,谈到退款就一直打马哈哈,只能赔偿一个配置超低,但是标价还比较好看的虚拟主机。

好吧,我服了你们了。

以前合作一切都没有问题的时候,很好都OK, 但是出了问题了,也就看清你们了。

]]>
Thu, 06 Sep 2012 01:56:14 +0000 viLuo http://viluo.com/post/39
如果说这是一篇测试文章,会通过么 http://viluo.com/post/38 Tue, 04 Sep 2012 07:54:49 +0000 ceshi http://viluo.com/post/38 百度收录更新规则 http://viluo.com/post/37 毫无疑问百度是我们国内搜索的老大,起绝对的流量优势,占据了搜索市场的70%份额,虽然饱受争议,但却无法掩埋他是大家网站收入一个重要环节。因此无论如何我们都得顺着百度的脾气。作为合格的站长,得多了解百度更新时间的规则,省去天天site的麻烦,多点时间去提高下SEO和完善网站功能服务。请问,百度搜索排名更新的时间你知道吗?在这里给大家说说百度的大致更新习性吧,如有不准确,请多批评指教。

百度收录的规则可以分为:每月2次大更新和每周小更新。

每周小更新规则:

星期一 : 提前为收录进行准备工作。一般百度在周四会进行一次更新,这个时候,利用星期一的时间好好的为收录做好准备,比如添加一些原创文章,增加一些友情连接之类的,对排名会有好处。

星期二 : 继续为收录准备吧。这个时候还没新增文章的朋友,记得去添加一些文章,可别让蜘蛛白跑一趟哟,如果蜘蛛发现你的网站不勤快,你的文章收录更新率可会大大降低!加油吧!

星期三 : 混乱的周三更新日。周三会有一次小更新,无论你的排名升了或者降了,都不要伤心或者兴奋,因为今天的更新是"不太准确"的,我们俗称“混乱的周三更新日”。引用网上例子:估计你也见过不少类似的。星期三的更新经常是比较混乱的。经常是无故出与些乱七八糟的排名。举两个例子。20多天以前,星期三的更新。朋友作的"视频会议"这上词。排名第二名的竟然是个域名到期的站。快照还是半年前的。打开之后。直接就是"该域名已经到期,点击这里进行续费"让很多人都感觉很是郁闷。都去抢注那个域名。

星期四 : 终于判断一周的大更新啦!每周的排名大更新,这是最紧张的一天。因为一般情况下,这一天的更新就决定了这一周的排名。如果这一天排名掉得厉害,那你只能下个星期继续努力了。当然也不排除有些特殊情况的出现,会让无数人摸不边。

星期五 : 没事,就无聊中去吧。星期四的结果一般不会变,或者极少会变,这时候站长们可以好好轻松一下,别一直窝在家里做网站,跟宅男似的,呵呵~

星期六 : 会有小更新哟!让不少人有点小担心的一天,因为这一天会有一次小更新。但不是星期三那种混乱局面。即使有变化也不会太大,可以把它看作是星期四那天的小小补充吧。

星期天: 好好放松下自己的大脑吧!周末啦,蜘蛛放假咯,你也给自己放个假吧。

以上是一周的更新时间,每天的更新时间一般都是凌晨四点到五点。

每月大更新规则:

百度每月大致有2次大更新,11号和26号/28号,特别是26号/28号,更新幅度最大,K站最多。

]]>
Tue, 04 Sep 2012 03:33:40 +0000 viLuo http://viluo.com/post/37
回望Java中Static关键字 http://viluo.com/post/36 工作两年以上的coder基本上都会常常用到static关键字修饰符,比如常用的工具类中我们会经常声明一些常用的static静态方法,如在JDK中的java.lang.Math类和java.lang.System类中的所有方法都被声明成为static方法;另外在程序中的全局常量经常也被声明成为static静态成员变量,比如一些固定的公式中的指定值,如自然对数的底数e为java.lang.Math.E,还有圆周长与直径之比pi为java.lang.Math.PI等。

对于我自己来说在实际的工作之中慢慢的能体会到在什么时候适合用static关键字,但是已经忘记了对static的真正理解,当对有的知识已经学会运用的时候,这个时候去反思为什么用,为什么这么用,在有实践的基础再去深入的理解他们我想会有更大的收获,你也许会体会到对于这些语言从整体上看前辈为什么会这样设计,出于什么目的以及他们的设计思想。所以我觉得思考是很关键的,用自己的脑子去思考……

static声明的有静态方法、静态成员变量、静态块、以及静态类,静态方法和静态成员变量又分别叫做类方法和类成员变量,试想为何在之前加一个“类”字?我们常用的成员变量和方法真正并不属于类,而是属于实例化的某个类对象,且每个实例化对象都有属于自己的成员变量。可见类方法和类成员变量并非属于特定的对象且特定的对象是在运行期间生成的,而它是属于类级别的在类被装载到内存的时候就被初始化了,跟实例化没有任何关系。被装载到的内存区域是一块专门存放静态变量的内存区域,可以叫做静态域,在《Thinking in Java》中对静态域叫做静态存储,是这样描述的:

静态存储。这儿的“静态”(Static)是指“位于固定位置”(尽管也在RAM里)。程序运行期间,静态存储的数据将随时等候调用。可用static关键字指出一个对象的特定元素是静态的。但Java对象本身永远都不会置入静态存储空间。

可以通过以下代码测试静态方法、静态成员变量、静态块、以及静态类的加载顺序:

public class StaticTest {
private String str = "hello world";
// 静态成员变量
private static String str2 = "hello";
// 静态块
static {
new StaticClass("test");
}
//静态块
static {
System.out.println(str2);
}
// 静态类
static class StaticClass {
public StaticClass(String str) {
System.out.println(str);
}
}
private  static String toSay(String str){
return str;
}
public static void main(String[] args) {
System.out.println(StaticTest.toSay("word"));
StaticTest test = new StaticTest();
System.out.println(test.str);
}
}

执行结果:

test
hello
word
hello world

可见在运行main主方法后,在调用System.out.println(StaticTest.toSay("word"));时就已经打印了test和hello,说明在类被加载到内存就已经执行了静态块的内容,根据test和hello打印先后顺序可以判断静态块是按照在代码中的先后顺序而执行的,如果静态块中涉及到静态类,则执行静态类的构造方法,然后才是执行main方法里的代码。

如果这一步理解到位了就能很容易的理解static了:它是类的公用全局变量或方法,对于类变量来说在类被加载时就被初始化了,始终只存在一份,存放于内存区的静态域内,跟类实例化对象没有关系,所以可以通过类名直接调用,正是因为跟实例化没有关系,所以在static方法中不能访问非静态成员(非静态成员必须依赖于实例化对象而存在),不会将对象的引用传递给它。

当真正理解了static后就能在应用中体会到它好处,首先毋庸质疑的是能很好的方便直接通过类名就能直接访问;另外通过static可以对全局变量作整体控制,维护一份static变量就能控制所有相关用到的引用;其次从内存空间角度来说在内存中只存在一份在占用内存空间和效率方面都有好处;还有是在运行期间之前可以加载一些后续业务的前提资源;但是也有人说由于static是面向类的而不是对象的,所以违背了面向对象的思想,然而事实上有经验的coder经常在使用static来编码。

原文链接:回望Java中Static关键字

]]>
Mon, 03 Sep 2012 08:45:58 +0000 viLuo http://viluo.com/post/36
领导综合症 http://viluo.com/post/35 viLuo:管理层的朋友们可以对对号,看看是不是真的有这么个领导综合症:-D

自从走上了管理之后,就发现自己慢慢的换上了领导综合征,而且症状越来越严重。

一.讲话增多

1.总结

任何会议,只要自己是这个会议里是相对的领导,或者没有自己的太跨层的领导,自己都要讲上几句,没事还搞个总结啥的。

2.容易插话

会议上发现自己感兴趣的东西,就容易插话进去,不管是提问,还是进行大幅讲解自己的见解。

3.旧有领域

如果有话题涉及到了自己熟悉的旧领域的话,很容易自我感觉良好,滔滔不绝,进行讲解。

4.容易讲道理

逮到了一个机会,就会向同事讲解道理,好像自己知道的很多一样,而且是不厌其烦,讲了一遍之后,换个机会,还会再讲一边。

二.插手过多

1.干预工作

不管工作进行的好与不好,没事就要干预一下,而且经常帮助别人设定目标,或者帮助别人设计东西。说的好听叫帮助,不好听可能就是强加。

2.主意过多

不知道哪里来的一堆注意,没事就要打断别人,说一下自己的想法,而且今天说一个之后,明天又会换一个。

3.意见较多

不管设计的多好,写的多棒,反正是上来就是挑毛病的,一堆的问题挑出来了,自己爽了,别人痛苦了,而且很多问题可改可不改。

原文链接:领导综合症

]]>
Mon, 03 Sep 2012 07:14:09 +0000 viLuo http://viluo.com/post/35
为什么总是缺人 http://viluo.com/post/34 为什么总是缺人,不管什么时候,都是一堆的任务等着人去做,人手总是紧张,难得偷得半日闲。知识密集型人才搞成了苦力密集型人才了。大家每天都在忙,难得抽出时间来培训,思考,反思,因为屁股坐在了火炉子上面,时刻都在煎熬着,跑的慢点了,就烤糊了。

中国有句老话,铁打的营盘流水的兵。说的很有道理,但是对于一个高速增长的企业,部门来说这个就成了问题,人员不稳定就会造成很多问题。

1.再培养

新招来的员工,需要熟悉很多东西,环境,人事,代码风格,框架代码,旧有代码阅读等等。这其中的浪费往往需要很多时间来弥补,可是任务不等人,事情还要做,只能靠人堆。以前可能一个人一个小时可以搞定的事情,变成了3个人一个星期的事。

2.知识断层

新员工到岗后,因为不熟悉原来的业务模型以及框架模型。导致设计,实现走的可能是复杂的路线,而不是最优的路线,并且由于新的设计可能导致模型的不匹配,导致返工或者桥接方式进行适配。这样平端增加出来的工作量可能是原来的2倍或者数倍以上,从而导致人手上的不足。每个行业后者领域都需要浸淫多年之后才能体会到其中的深奥之处,而这些都是无形的知识和经验,新员工到来后,这层知识就已经消失,只能从新来过。知识没有通过新老交接,逐步沉淀下来。

3.无人可用

老员工的频繁流失,当有新的业务或者领域开展之时,手下无人可用,手下皆是一年及以下左右的新手,现有的业务还未完全熟悉,此时委与重任又不放心,可是又无可用之才,缺人之感倍增。只能矬子里面拔将军,而此人的能力又将决定实现的难易以及复杂程度,往往又是造成劳动密集型的局面。

4.恶性循环

劳动密集型的局面,造成员工没有时间进行思考和进修学习,导致大家每天都是在疲劳工作,效率下降。效率变低之后,就会产生工作量过大的局面,从而又导致缺人的局面产生。当人员没有补充上来,大家继续疲劳工作,效率继续下降,工作量也就越显越大。即使新员工入岗,又会面临上面的问题。同时又因为疲劳工作,并且没有提升空间等原因,很多员工可能选择离职,而这些员工往往都是骨干员工。从而使缺人的现象产生了一种恶性循环。

5.招聘质量

因为对于招聘的漠视和一些种种限制导致了招聘进来的人员素质水平不是理想状态,大家往往只追求了量上的满足,而不是质上的满足。人员也没有呈现梯队化,导致培训成本的升高和后期提升速度较慢。这样也造成了工作效率不高的一个方面,从而导致工作在一段时间内产生积压。

铁打的营盘流水的兵,当流水的速度超过一定的限度的时候,就会造成很多不良效果,当然流水的速度如果继续上升的话,铁打的营盘也会落入湍急的漩涡之中。拥有知识密集型的人才,就应该做到让人才的知识密集起来,而不是让人密集起来。稳固流水的速度,提升员工的生产力和知识能力这样才能慢慢解决缺人的困局。

原文链接:铁打的营盘流水的兵---为什么总是缺人

]]>
Mon, 03 Sep 2012 07:09:33 +0000 viLuo http://viluo.com/post/34
最佳实践的难能可贵 http://viluo.com/post/33 参加了多次的培训之后的最大的感触是,一群道貌岸然的大师侃侃而谈,讲述着各种的规范,各种原则,口若悬河,讲的天花乱坠,但是没有一样直指内心,全是纸上谈兵的泛泛之论,如果询问落地的细节之处时,言语含糊,顾左右而言其他,或者以时间有限为由搪塞而过。

静下心来仔细想想,也是,各种大师,讲师,砖家,布道之人很多都是被一门理论进行灌输,深入学习了这种理论,对于理论的细节之处可以侃侃而来,但是这些都是仅限于知识理论之内,因为没有实地操作过,实际经历过,所以落地之事就比较含糊其词,不能讲的很明确和详细。

就这样,理论在天上飘着,下面的人儿看着漂亮的云朵变换着各种美妙的图形,追逐着绚丽的虚幻,而忙来忙去总也不能落地,或者迷失方向,或是望而兴叹。云朵和大地之间需要建立一座桥梁,这座桥梁就是【最佳实践】,这些最佳实践可能来源于两个方向。第一个方向就是那些大师沉淀下来,真的走进现场,拿着理论指导实践,从而总结出最佳实践。另外的一个方向就是底层人员提升上来,努力的够着那些漂浮的云,升华着自身实践。

只有这些能上天入地的实践才是最有意义,这有这些人走出来的路才是我们最需要的指引,期待着更多的最佳实践,期待着下里巴人的提升,也期待着阳春白雪的落地。

有些时候转眼想想,感觉到很是悲哀,多少的专业人士不是来自现场,多少无知顾问对于现场指手画脚,多少的培训讲师在上面侃侃而谈,说着正确的废话,多少的辛苦劳作者被一群砖家教育着,被那些吹的天花乱坠的理论所迷惑。

]]>
Mon, 03 Sep 2012 06:56:29 +0000 viLuo http://viluo.com/post/33
离职你真的想好了吗? http://viluo.com/post/32 viLuo:比较推崇这么一句:如果追逐钱财的话,那么你一直在钱财的后面追逐,反过来,保持内心的平静,努力提升自己,钱财自然回来追你。现在的大环境是比较浮躁的,需要沉淀自己。

又到了春天,也是大家开始规划一年的时候了,很多人也都是这个时候踏上了找工作的征程中。那么离职找工作这件事情,你是否真的想好了?离职目的根据我个人经验大体划分为四种,经济待遇提高,能力提升,平台,创业。

一、经济待遇提高

大多数人都是逐利,人为财死鸟为食亡,这点无可厚非。但是如果仅仅是为追逐钱财的话,对于个人的发展可能会造成不利。很多人都是在感觉各种各样的问题时候,寻求离职,而这个时候,如果只是为了钱财离职的话,你会发现即使薪资提升上去了一些,但是自己所面临的问题却依然存在,而且没有减少,可能还会增加一些困惑。比如如果去那家公司可能会更高一些,刚到了一家,就会被另外一家所诱惑等等,心中总会毛毛躁躁的,一时不能踏实下来。而之前在公司遭遇的心境,问题,在新的公司里面,你会发现,是一样的出现,这个时候,你可能会感叹一句话,“天下乌鸦一般黑,到那里都是一样。”,并且离职仅仅为钱的话,就会造成短视效应,也就是只看到了眼前的利益,而忽视了长远规划,导致头两次的跳槽可能会得到部分薪水提升,但是后面跳槽却越来越乏力,不会产生新的增长。

我曾经的经理对我说过这样一句话,“如果追逐钱财的话,那么你一直在钱财的后面追逐,反过来,保持内心的平静,努力提升自己,钱财自然回来追你。”我觉得,这句话的上半句说的就是第一种情况,下半句说的就是第二种情况。

二、能力提升

还有一些人是因为在公司内遭遇一定的瓶颈,主要是能力提升瓶颈,这个时候选择离职,更多的是为了将来的打算。主要考虑是更多的大公司,或者能够发挥自己能力的公司去打拼,这个时候离职就相对理智,可以不为一时利益所诱惑,寻找到相对满意的工作地方。只是去了对应的工作环境后,是否会能如自己所愿,获得相应的能力提升,就需要多方位考察,仔细询问相关人员工作内容,工作方向等。

另外就是,现在的公司里面真的没有提升能力的地方了吗?在现在的岗位上你是否发挥出你的能力和才华了?不要总是寄予于换个地方就能发挥能力,更多的时候是,如果这个地方你没有充分发挥你的才华能力,下的地方你可能还是没有发挥出能力。外界的因素还是外因,更多的动力源自你的内心动力,那个才是你真正的内因。在现在的环境里面,更多的是你熟悉的人脉,环境,也是你更容易发挥的平台。所以在寻求更广阔的平台前,需要你驻足想想你的现在,再做决定也不迟。

三、平台

如果能力有了一定的积累,但是职业发展遇到瓶颈,那么寻找一个好的平台对于自己来说就很重要。这个平台能够充分发挥自己的特长,平且还能更多的展示出自己的能力,使自己的能力可以更大的发挥。自己的知名度,视野也都会有相应的提升,到达这个平台上之后,自己所见,所闻,所了解的都会发生变化,同时自己的路也从原来的羊肠小道变成四通发达的宽阔大路。平台可能是一个人,一个公司。一个在业界有很大知名度的人或者公司,可以给你带来的平台是你无法想象的,在这个舞台上你可以了解更多的业界知识和更广阔的人脉。这样好的平台需要你去慢慢寻找,很多时候,可能可遇不可求。

上面说的三种都需要兴趣和志向所支撑,如果没有兴趣和志向给与再大的平台,都不会闪耀出璀璨的光芒。

四、创业

创业也分很多种情况,加入创业公司,独立创业,微创业

1.加入创业公司

如果只是那些财富神话吸引着你加入创业公司,你加入后,等待着facebook,baidu,google的上市梦想,期待着一夜暴富的情况,那么劝你还是冷静一些。《》因为你更多的是面临着还没等公司上市,你就已经熬不住,提前退出公司了,或者即使上市了,如果你不是大股东的话,你也只是拿到了那微不足道的小小分红。

所以加入创业公司需要你进行了解创业公司是否是你的兴趣和志向,是否是你理性的方向,只有这样,你才能耐得寒苦,熬得一缕梅香。

2.独立创业

和加入创业公司一样,是否只是想为了一夜暴富而来,是否那么多融资公司让你眼热,而一时心起,加入了创业大潮,等你加入了创业大军之后,你才会发现,开局不是花花绿绿的钞票向你飞来,而是各种各样的问题让你一夜白头,按下葫芦浮起瓢的事情,让你焦头烂额,不知道你到时候会不会后悔自己的一时冲动。只有真正的理想才能是你坚持下去,熬过那最冷的寒冬,同时理想还要和现实结合起来,才能得以延续发展。

3.微创业

我个人最推崇的是微创业,在你现在的工作环境中,将你的产品当做你创业的事情,努力做好,考虑它的市场贴合度,考虑它的售价政策,维护政策。另外是否可以抓住公司里面的新机遇进行微创业尝试,比如新的部门成立之时加入,或者发现新的产品方向的时候,努力争取,还可以把自己的环境向创业方向打造。在公司里面打造一片自己的天地,这样的好处就是可以节省创业的风险,可以体验创业的问题。不好之处,你的创业成果归公司所有,不过可以想象的时候,如果你做的很好的话,你可能也已经获得了你所想要的待遇。

所以,离职你真的想好了吗?

原文链接:闲言碎语话心得--离职你真的想好了吗?

]]>
Mon, 03 Sep 2012 06:32:40 +0000 viLuo http://viluo.com/post/32
从研发经理到部门经理——一个新经理的困惑与求解 http://viluo.com/post/31 去年年初的时候,刚刚由一名开发经理,提升为一名三级部门的部门经理,喜悦之后,困惑随之而来,而且应了那句老话,福无双至祸不单行,困惑,迷茫也都是接踵而来。

一、大开发经理

职称发生了变化,那么都应该那些地方变化?部门经理和开发经理那些不同?这些困惑在一次领导对我的点评时冒了出来,领导说,你的心态要变化一下,现在感觉你就想一个大的开发经理,而不是一个部门经理。这句话引发了很多的思考和反思,作为部门经理做的工作确实和开发经理差不多,还是以前的老路子,但是这两个职位之间到底有什么不同,到底应该怎么做?

二、拳要离手,曲要离口

因为要忙于很多管理事务,并且因为部门是新成立的部门,16个人的编制,年初的时候只有4个人,大量的招聘工作也是占据了大量的时间,还有各种的事情,使自己没有办法进入到研发状态。台上十分钟,台下十年功,那么多年的功力,在进入的管理的岗位,逐渐的被荒废,心中多少有些不舍和心痛。

三、有力发不上

更多的关注管理上的事情之后,新技术的使用,调研,研发都已经变成了团队里其他成员的事情了。而新技术的使用往往又不是一帆风顺的,当项目上,产品发版时新技术出现问题的时候,自己往往有些束手无策,看着干着急,只能从自己的经验和感觉上给与一些指导。很想捋起袖子,甩膀子干,但心有余而力不足。

四、单纯不单纯

纯研发人员,只要处理好安排好的任务就好,不用考虑太多。现在是猛然间遇到更多方面给与的压力和问题。这个项目要求紧急支持,那个项目新的需求要求尽快对应,这个地方人员不能回来,需要继续维护,人员缺少,而产品发版还要按时,项目使用技术需要寻求各个部门支持,项目需要安抚客户情绪等等,接触的面一下子变得广了起来,而且事情也变得多了,事情安排,重要性排序,时间管理就凸显的很重要了。

自己思考了很久,很多困惑没有得到解决,而且有些迷茫和痛苦。年初刚刚上任的时候就向领导咨询过,我应该如何去做,领导告诉我要以一颗经营的心去管理一个部门。当时听了之后,不置可否,如何经营,我没有实权,收入不归我,奖金,工资,福利全都不是由我负责,我怎么去经营?这两个字一直盘踞在脑中,慢慢在品味,琢磨。临近年中的时候,领导又推荐了一本书《目标-简单而有效的常识管理》。《目标》这本书主要讲的是一个濒临倒闭的工厂经理,在不同的时候,寻找不同的目标,并根据目标去寻求什么有效产出,从中发现瓶颈和无效产出,最终使这个工厂起死回生,成为公司内业绩最好的工厂。看了这本书之后,心中产生了很多想法,感悟了很多,也试图把书中的理念和我思考的问题进行对照,进行反思。确实,我的目标又是什么?对于我而言,什么是有效产出?问题变化了,但终归还是雾里看花,水中望月。不错也有些混沌的理解,领导所说的经营的含义。于是带着想法继续迷茫的探索着自己的路。

转眼又是一年,又到了年中总结的时候了,很多疑惑已经有了自己的答案,对与不对也无从可知,只是自己的管理之路已经逐渐在路上了,在这里和大家分享一下自己的拙见。

开发经理和部门经理的区别主要在于二者的目标不同,所以二者关注的有效产出也是不尽相同的。作为一个部门经理,在公司中的首要目的就是提高产品收入,增加公司利益,所有有利于提高产品收入,增加公司利益的事情才可能是有效产出。这就要求部门经理去提高产品质量和有效功能,快速响应项目和市场要求,做出贴合市场,并且符合市场发展的产品。就要去贴近一线的客户,倾听市场的声音,并且制定产品的发展方向,和部门的整体走向。明白了目标的不同,开发经理和部门经理的区别就显而易见了。当然部门经理还要进行部门的整体经营管理工作,维护好整个部门的利益和发展,员工的发展路线和部门路线的匹配,部门整体部署,应战能力等等管理工作。

另外职位的变化,也要对应心理变化,舍得二字,有舍方有得。有些东西固定要被舍弃,只是舍弃有不同的方式和方法,不是一下子就抛弃了,而是把经验和知识传递下去,做到落叶归根,拳虽离手,曲也离口,但是这些经验能力都要交给还在研发战线上的兄弟们。另外这些经验技能对于产品规划和产品发展来说,还在发挥着判断,辅助的功能,也并不是全然无用。

有些地方自己是无从使力,但作为一个部门经理来说,需要做的是全盘考虑,并且一个真正的团队并不是靠个人英雄主义发展的。自己要学会放手,让员工快速发展,这样的团队才能快速发展。一切发展还要靠团队里面的所有人,自己不能发力,但是要让团队的成员发好力,发对力,部门经理进行指引方向和给与支持。要扶持员工快速发展和接触更多的新事物。

要想经营好一个部门,就需要管理去应付方方面面的事情,考虑风险,协调资源。需要全面考虑财务指标,市场动态,流程梳理,提高有效产出,增加市场占有率等。那么这些肯定需要去面对各个方面,只有积极的去面对了,才能更好的发展部门。客户的意见,批评,指导都是产品发展的源泉。做好一个产品仅仅靠自己部门的单打独斗也是不行的,就需要去进行协同作战,资源调配好了,才能更快更好的交付项目,研发产品。

明白了目标,那么困惑自然就会随着消失。一个困惑消失的时候,一定会有另外的困惑产生,这个时候需要不断的去寻找自己的目标,寻找自己的有效产出。管理的路漫漫,作为新的管理者还有更多的困惑和迷茫需要面对,但是不管什么样的路,都需要有乐观和探索的心态亦步亦趋的前行。

原文链接:从研发经理到部门经理——一个新经理的困惑与求解

]]>
Mon, 03 Sep 2012 06:26:54 +0000 viLuo http://viluo.com/post/31
Why Normal--牛人都干啥去了? http://viluo.com/post/30 “让我怎么说呢?你们的程序也就凑合用吧”,恩,程序也就是凑合着用,没有好没有坏,没有什么出彩,仅仅是可用而已。为啥程序仅仅是可用,而没有达到良好,那么多程序员怎么还是这个水平?做了这么多年,还是这样?开发一个小需求还需要这么长时间?说什么好呢?算了还是不说了。

为什么?为什么我们做的程序这样普通,为什么我们做不出精彩的产品,令人激动过程为啥我们没有体验到呢?想想我们的牛人们都在干啥!那句伟大的话道出了这句玄机,“你们稍微牛一点的人都去做管理了。”

我们现在的组织结构是这样的

点击查看原图

牛人或者类牛人做了几年可能就转到了管理岗位上了,编程工作就移交给了新来的小弟弟,小妹妹们,之前的业务经验,技术经验就直接出现了断层,负责的人呢,就慢慢的把经验向下渗透,但是渗透的时间往往需要1~2年,而这几年的时光里,下面的人或者也挤进了管理岗位,或者流失走了。

我们大多数的时候,或者企业。会把管理职位当做一种奖励机制,经常会鼓励某些潜力份子,“好好干,年底的时候提拔你做研发经理。”,“恩,你会成为部门经理的候选人”等等,这样导致了大量的研发经验好的牛人走上了并不熟悉的管理岗位,而把那些空缺留给了实习生,应届生。做了2年就当上项目经理,当上研发经理的比比皆是,表面上风光无限,实际上就不得而知。

当然有些时候也是无奈的选择,不走管理岗位,那么就不要想着加薪,或者不要想着大幅加薪。一个干活的拿到了1~2W的薪水,有人会惊呼,“这么多。”,一个管理职位的拿到1~2W的薪水,也会有人惊呼,“才这么点,不可能吧。”,是啊,世俗的一些观点或者HR的观点导致了干活人员拿不到对应的薪水,被逼上了管理之路。还有就是,“那家伙这么大岁数了还在编程”等等,依然困惑着那些徘徊的人,或者那些准备走另类路线的人们。

对于公司而言,真正工作的,都是那些苦逼的应届生,实习生,那些new boy,产品的质量,产品设计,产品的整体水平,研发的效率如何不是normal,如果不能是这样的庸俗,对于这样的现状你还能有多少的期待呢?如果不能制定出对应的研发体系薪金标准,不能提高研发人员的薪水水平,那么我们只能接受这样无奈的结果。每个人做着不是自己喜欢的工作,就这样平庸到老!

原文链接:Why Normal--牛人都干啥去了?

]]>
Mon, 03 Sep 2012 06:17:30 +0000 viLuo http://viluo.com/post/30
CSS盒子模型小剖析 http://viluo.com/post/29 前段时间刚刚从C/S过度到B/S,提到B/S就不能说CSS,而说起CSS又不能落下盒子模型。在CSS诞生的时候就有了盒子模型的概念,网页中大部分的元素都能构成一个盒子模型,。盒子模型无非就是描述的元素的尺寸跟位置,只要掌握好这两点就可以灵活的运用盒子模型来布局了。下图是盒子模型的基本元素和属性:

点击查看原图

下图是一个盒子模型的基本模型,网页中每个元素大概都可以用下图的模型来描述。

点击查看原图

element: 元素。有的地方也叫做content,译为内容。

padding: 内边距。默认值为0,不可以为负值。具体使用如下:

padding: 10px 20px 30px 40px;   /*设置元素内边距的上、右、下、左的值分别是10、20、30、40像素*/
padding: 10px 20px 30px;   /*设置元素内边距的上、右、下、左的值分别是10、20、30、20像素*/
padding: 10px 20px;   /*设置元素内边距的上、右、下、左的值分别是10、20、10、20像素*/
padding: 10px;   /*设置元素内边距的上、右、下、左的值均为10像素*/

可见,如果对四个方向均进行设置,那么就按设置的值,如果只设置上、右,那么下、左默认为上、右的值,如果只设置一个值,那么四个方向都为此值。另外四个值也可以单独设置,对应属性为padding-top、padding-right、padding-bottom、padding-left。

margin: 外边距。默认值为0,与内边距不同的是它的值可以为负值,使用方法与内边距相同,不多说。

border: 边框。用来设置边框的粗细、样式、颜色等属性。

/*设置元素边框样式为实线,颜色为黑色,粗度为20像素*/
border-style: solid;
border-color: black;
border-width: 20px;
/*设置元素上边框粗度为15像素,左边框样式为双线,右边框颜色为红色*/
border-top-width: 15px;
border-left-style: double;
border-right-color: red;

注意:如果上下文设置有冲突,则按照下面的设置,以上代码的效果为上边框为15像素,其他边为20像素,其他属性同理。

width: 宽度。指的是element的宽度,不是盒子的宽度。

height: 高度。指的是element的高度,不是盒子的高度。

盒子的尺寸根据下图应按一下方式计算:

点击查看原图

盒子宽度=width + padding-left + padding-right + border-left-width + border-right-width

盒子高度=height + padding-top + padding-bottom + border-top-width + border-bottom-width

了解了以上内容,盒子模型基本就没问题了,掌握了盒子模型CSS也就差不多了,剩下的无非也就是将一些基本的属性合理组织,配合使用了。只要多练习想要调出美观大方的CSS样式不是什么难事。

]]>
Fri, 31 Aug 2012 09:03:23 +0000 viLuo http://viluo.com/post/29
程序猿的九重天 http://viluo.com/post/28 每个职业在成长中,都会属于某一阶段,有着困惑和希望,作为程序员的你,对比一下到哪种境界了,本文给你对对看。

第一重:

在哪本电脑杂志上看见软件工程师这个职业介绍,或者走在大街上看见某某软件培训机构鼓吹培训完就能年薪10万的广告,有些羡慕那些编程序的,自己有些蠢蠢欲动。

第二重:

用控制台或者说SDK之类的东西写了一个“HELLO WORLD!”,感觉编程这个东西,太简单没难度。

第三重:

学会了简单的使用某某工具例如VC,知道怎么利用工具创建对话框之类的东西了,认为自己已经有了“扎实”的基础,出去找工作四面碰壁之后感叹人心不古世态炎凉。

第四重:

以极低的要求找到工作,突然发现自己不会的实在是太多太多,不求甚解是无奈之举。这个阶段写代码写完之后主要是试,试的标准:“能运行就是成功,不能运行就是失败。”一个小模块写完之后被黑盒蹂躏几十次稀松平常。

第五重:

突然意识到不能再瞎干了,于是知道了如何规范的写代码,尽力的把代码写的短小精悍,看一堆关于算法的书,并且会把自己的或者别人的优秀代码分门别类存起来,以便经常参考使用。

第六重:

做的时间越长,发现自己不会的越来越多,每每遇到不太理解的必然刨根问底,写完一段代码,一定仔仔细细把程序研究个透,不让他出现一点泄漏一点警告才罢休,以挑战白盒作为乐趣。

第七重:

把各种工具研究个稀巴烂,即使一个全新的语言或者工具,也能在半个月之内熟练使用,这个时候,仍然认为自己距离高手还有很大差距,遇到新手来公司面试,很惊讶这些人居然连怎么操作内存都不会就敢出来混了?

第八重:

把编程工具用烦了,开始喜欢控制台或者说SDK,纯粹的API编程变成了自己的最大乐趣。

第九重:

在电脑杂志上看见软件工程师的职业介绍,或者走在大街上看见某某软件培训机构鼓吹培训完就能年薪10万的广告,想对后来人说些什么却终究发现无话可说,只好一笑而过。

第一重到第四重,需要努力,第五重到第六重,需要悟性,想到第九重,需要智商和福缘。

]]>
Fri, 31 Aug 2012 08:44:10 +0000 viLuo http://viluo.com/post/28
程序员的九阳真经 http://viluo.com/post/27 1. 不要为过去买单

不要活在过去,今天才是最最重要的,明天更重要。

不要弥补过去,那样会错过今天和明天,更会把自己迷失在过去的阴影中,会造成总是犯错,又总是为过去改过,于是生活在一个怪圈之中。

2. 量化目标,把目标具体化

千万别把一些:掌握,熟悉,高级,专家等一些非常抽象的标准作为你的目标,仔细想一想:什么叫掌握,什么叫熟悉, 什么叫高级什么又叫专家呢? 这些东西不同的人会有不同的理解。

要用具体的,实际的,可测量的量化目标,比如今年年终时存款达到什么样的数目; 再如明年的月薪达到什么样的水平;今年读几本书; 今年写多少篇博客;这个月做一个应用程序;参加几次社区活动;每周发贴数不少于10条等等。

这些具体的量化目标非常之明确,你能否达到,以及你与目标之间的距离也非常之明确,所以实践起来更容易。

3. 不要做太长远的计划

如上一条所说,千万别做太长远的计划,最好别超过5年。 现令世界变化太快,人生的变数也很多,说句老实话凡人看不到太远的未来。

其实就连乔布斯,盖茨等人也没有超过三年之后的详细计划。 当然,我们要有梦想和想做的事,但是详细的计划最好别超过三年,否则等三年之后你会发现那些当初的计划非常好笑。

4. 关注过去,还是着眼未来

这是面试时要考虑的事情。

在面试的时候面试官总是不厌其烦的问:你做过什么项目,你解过的有成就感的问题是啥,你学过什么技术,你看过什么书,你解过什么Bug等等。 但是我们需要的人是能解决未来的问题,能做好未来的项目,但是未来会遇到哪些问题,谁也没知道。

乔帮主和苹果的团队的创新能力无人能及,他们创造出了很多的改变了世界的伟大产品, 他们的产品领先于世界至少5年,但你问他们未来5年的手机会是什么样子?他们也不知道。 伟大的产品都是要经过长期的摸索,试验,克服数个困难才会形成的,而不是一下子就能想到的。

这其中更为重要的因素是弃而不舍的努力和不断尝试的勇气, 想做伟大产品的决心和敢为天下先的魄力,跟过去做过什么,跟过去解决过什么问题,其实不太相干。

5. 定期清空杯子

如果杯子满了,就再难装入新鲜的水。 很多时候,感觉不再有激情了,感觉乏味了,感觉没有新鲜感了,感觉不想看书了。

这个时候就需要清空杯子,把自己当成一个小孩子,当成一个学生,当成一个刚毕业的孩子, 当成什么都不懂的人,从头来过,从新开始学习。 会有意想不到的收获。

另外就是,当找不到问题的突破口时,也需要抹到一切线索和思路,从新再次分析问题。 就好比解数学题一样,如果乱了,解不下去了,就要忘掉先前的思路,休息一下,然后重新审题, 重新思考,回归基本,之后便会有新的思路出来,没准就会找到突破口。

这还有一个好处就是可以保证你不会走偏,因为很多时候由于没把问题搞清楚,加之心急,导致思路混乱,偏离了真正的问题,于是越来越乱。 这时就需要停下来,重新理清问题,重新思考。

6. 个人仅做小项目,对于团队大项目,只能参与开源项目

我们的大问题也是缺少项目经验,但是个人的力量单薄,时间有限,只能完成小项目,甚至是几百行,几十行的小程序。

对于团队协作方面,只能参与开源项目,或者参与社区。

7. 给任何项目(任务)一个时限

就像编程比赛那样,给每个项目一个时限,有压力才会有动力,这样才能让自己进步。

几个小时完成一个小工具,几天完成一个小应用等等,像ACM或者编程马拉松,或者开放编程比赛那样, 二三天内做出一个应用,这样会有成就感,更会学到很多。

8. 参与Topcoder,ACM/OJ等

参与Topcoder,ACM/OJ等,保持算法,分析问题和解决问题的能力。

9. 定期检查自己

像学生时代的考试一样,定期的来个面试,或者与老朋友畅谈,以回顾和检查过去这一段时间或一年的得与失和生活的收获。

特别是对于经验和技能被看重的IT行业,定期检查尤为重要。

建议至少一年内要进行一次或几次面试,通过面试,你能从一个侧面知道自己的进步与不足之处,便能知道下一步该如何去做。

更重要的要,通过面试,你能知道自己的不足之处,与理想职位之间的差距,和要努力的方向。

]]>
Fri, 31 Aug 2012 08:35:33 +0000 viLuo http://viluo.com/post/27
JVM内存结构浅析 http://viluo.com/post/26 JVM 包含两个子系统和两个组件。两个子系统分别是ClassLoader和ExecutionEngine,两个组件则是Runtime Data Area和Native Interface,其中Runtime Data Area中就包含了我们常说的JVM内存。

点击查看原图

但仅仅将内存分成这么一大块,JVM是啥也干不了的。为此Runtime Data Area中又将内存划分成不同的区域,用于存储不同的数据。

Method Area——方法区,被Class Loader所装载的class文件以及相关的方法信息、域信息、静态变量等都存放在这个区域内。该区域是所有Java线程所共享的。

Heap——堆区,这个区域就是用来存放java对象的,通常GC也是针对该区域。一个Java虚拟机实例只有一个堆,并直接由java虚拟机进行管理,在虚拟机启动时创建。该区域可以被所有Java线程所共享。

Stack——栈区,用来存放JVM的内存指令。通常虚拟机对它的操作比较简单(以帧为单位的压栈和出栈),速度也很快。每个线程都有自己的栈,且栈可以不连续。

Program Counter Register——每一个线程都有自己的一个PC寄存器,用于存放下一条被执行的指令的地址。每个线程的PC寄存器在线程启动时产生。

Native Method Stack——保存本地方法进入区域的地址。

通常来说,也可以将JVM的内存区域划为堆和非堆两个区域。堆即上面的Heap区域,剩下的都是非堆区域。而jvm 对内存垃圾的回收,即GC,通常都是针对堆区域的。

]]>
Fri, 31 Aug 2012 08:27:46 +0000 viLuo http://viluo.com/post/26
JVM对大对象分配内存的特殊处理 http://viluo.com/post/25 前段日子在和leader交流技术的时候,偶然听到jvm在分配内存空间给大对象时,如果young区空间不足会直接在old区切一块过去。对于这个结论很好奇,也比较怀疑,所以就上网搜了下,发现还真有这么回事。以下给出具体代码来说明:

首先定义好jvm内存各个区域的大小。我设定的是eden区8M,from和to各1M,old区10M,总共20M的空间,参数如下:

-Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8

紧接着,开始写程序。很简单,就是初始化一个9M的程序,然后用jstat命令看jdk的内存使用情况。

public class App {
private static final int _1MB = 1024 * 1024;
/**
* VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8
* -XX:PretenureSizeThreshold=3145728
*/
public static void main(String[] args) {
byte[] allocation = new byte[9*_1MB];
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

然后打成jar,执行。结果如下:

S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT
0.00   0.00  18.04  90.00  23.08      0    0.000    20    0.027    0.027

果然,当对象大小大于eden区的时候会直接扔到old区。但我还不满足与此,于是将对象改大了些,改成了11M。再次尝试发现结果如下:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.taobao.jdkmem.App.main(App.java:17)

到这里结束了么?当然没有:)这个是一个大的完整的对象,当大对象本身是由一连串的小对象组成的时候,会不会不再OOM呢?于是改了代码再次尝试:

public class App {
private static final int _1MB = 1024 * 1024;
/**
* VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8
* -XX:PretenureSizeThreshold=3145728
*/
public static void main(String[] args) {
byte[][] allocation;
allocation = new byte[11][_1MB]; // 直接分配在老年代中
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

再次运行,结果如下:

S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT
0.00  38.06  67.68  60.02  23.10      1    0.007    14    0.012    0.019

果然,这次居然又被jvm给生吃下去了。不过这次并非所有的都在old区,而是有一部分还在young区里活着。看来jvm还是足够彪悍的。

由此可见,当出现大对象的时候,jvm用牺牲部分宝贵的old区的方式来保证了整个jvm的正常运转。所以,程序中尽量要避免大对象,如果实在不行,就让大对象活的尽量久些,莫要new一个然后gc掉再new一个再gc,这么爆jvm可不太友好。

到这里结束了吧?你猜对了,还没有:P既然知道jvm会对大对象申请内存做特殊处理,那么就在琢磨程序员有没有方法干预这个过程呢?答案是有的,就是使用这个参数-XX:PretenureSizeThreshold。这个参数的单位是Byte,其作用是当新对象申请的内存空间大于这个参数值的时候,直接扔到old区。做个试验就证明了:

public class App {
private static final int _1MB = 1024 * 1024;
/**
* VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8
* -XX:PretenureSizeThreshold=3145728
*/
public static void main(String[] args) {
byte[] allocation = new byte[4*_1MB];
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

运行命令如下:

java -jar -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=3145728 memtest-1.0-SNAPSHOT.jar 

我设置的阈值是3M,意味着超过3M的对象会被直接扔到old区。结果是皆大欢喜,新对象直接被扔到了old区:

S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT
0.00   0.00  18.04  40.00  23.08      0    0.000     0    0.000    0.000

试验有了结果,自然而然心情愉悦。但这个参数使用时需要慎重,因为fullgc的代价很高,因此old区就显得非常宝贵。除非你真的清楚你在干什么,否则莫要轻易玩这个参数,万一搞个频繁fullgc就玩大了。ok,到此打完收工。

]]>
Fri, 31 Aug 2012 08:12:44 +0000 viLuo http://viluo.com/post/25
JVM内存结构浅析 http://viluo.com/post/24 jvm中的内存可以分为堆内存和非堆内存,其中堆内存用于存储虚拟机运行中产生的各种对象,而通常所说的GC也就是针对这一块内存而言的。作为开发者,我们并不需要去关心到底什么时候该调用GC去清理内存,因为jvm会帮我们打理好这一块。但是如果想深入了解java内存管理,就不得不关心下这块对程序员“透明”的区域了。

要了解GC过程,首先要说说堆内存是怎么工作的。java所创建的对象会都放在堆内存中,但随着新对象的逐渐加入,堆内存的剩余空间会越来越少。当无法给一个新的对象继续分配内存空间时,程序就会发生out of memory。我们知道,java程序员在编写时无需关心何时释放对象的内存,所以会有相当一部分的“垃圾”对象,即再也不会引用到的对象存在于内存中。当剩余空间不够时,jvm就会扫描堆内存找出垃圾对象,将其占用的内存释放掉,这就是GC。

jvm中的GC采用了generation算法,认为内存中的对象有这样的情形:大多数的对象存活的时间比较短,而少部分的对象才能够长时间存活。因此,jvm将堆内存划分为年轻代(young generation)和年老代(old generation)。年轻代中的对象通常建立时间不久,且大部分生命周期也很短;年老代中的对象则已经创建比较久了,其声明周期也相对年轻代比较长。按照上面的划分,jvm在做GC时也进行了区别对待,对年轻代GC会相对比较频繁,且采用了copying算法;年老代的GC相对比较少,且采用的是tracing算法的一种,是标记-清除-压缩。

具体结构如下图:

点击查看原图

Young Generation——当一个对象被创建时,需要给其分配内存。由于堆内存是所有java线程共享的,所以如果要分配一段内存空间就必须先对内存加锁,这样是很耗性能的,故如果对象所需内存小于某个值(好像是512个字节,我也忘了)直接从线程自己的缓存里分配。如果在Young Generation中分配内存的话,就通常是放在Eden(伊甸园区,不知道为啥取这个名字)中。这里就要介绍下Young Generation中内存的划分和使用了:

Eden——所有新创建的对象都被放置在这里。

Survivor——当Eden区空间不足时,会将其中依旧存活的对象拷贝到两块Survivor区域(From Space和 ToSpace)中的一个,如果此时这个Survivor区域也空间不足,则将该块区域中存活的对象拷贝到另一块区域中。 注意,总有一个Survivor区域是空的。

对Young Generation的垃圾回收叫minor GC,通常很多的对象都活不过一次GC。

Old Generation——但一个Survivor区域满了的时候,会将该区域中已经历一定次数GC而依旧存活的对象放到Old Generation中。如果Old Generation也满了,那就要Full GC了。Full GC很耗性能,当Full GC进行时,应用程序会暂停。由于大部分对象都活不过一次GC,所以如果服务器上频繁的发生Full GC,就要关注下是不是出问题了。

Permanent Generation——这块区域我认为就是方法区,其作用就是用来存放java类、方法信息和常量池等。有的观点也将方法区看做是Permanent Generation,并看做堆内存一部分,这里就顺便介绍下。通常这块内存不会有什么太大的变动,当然如果采用了动态生成一些类的方式来设计应用程序,那么这块也需要设置较大一些用来放置新增的类。另外,由于常量池也放在这块区域里,所以如果常量池无限增大,理论上也会出现oom。也正因为这样,这块区域也会有GC,而且方式是Full GC。

上面就是jvmGC的一些信息。当然,开发人员也可以通过设置参数来影响jvm对内存的管理,下面就是一些常用的参数:

-Xmx——Heap区域最大值。默认为系统内存的1/4,不超过1G。

-Xms——Heap区域初始化时候的内存值。默认为系统的1/64,不超过1G。如果当前的空余堆内存比例小于40%(见-XX:MinHeapFreeRatio)系统会将堆内存扩大到最大值。通常-Xmx和-Xms设置为一样的值,免得内存来回变动损耗性能。

-XX:PermSize——设置方法区的内存初始值,通常为系统内存的1/64。

-XX:MaxPermSize——设置方法区的最大内存值,通常为系统内存的1/4。

-XX:MinHeapFreeRatio——设置Heap区域最小空闲值,用于控制何时将堆内存扩大至最大值,通常是40%

-XX:MaxHeapFreeRatio——设置Heap区域最大空闲值,用于控制何时将对内存缩小至初始值,通常是70%

-XX:NewRatio——设置Heap区域中new和old代大小的比例,值就是年轻代内存大小/年老代内存大小。

-XX:SurvivorRatio——设置Survivor区域和Eden区域的比例,值就是Eden区域大小/Survivor区域大小。

]]>
Fri, 31 Aug 2012 08:06:06 +0000 viLuo http://viluo.com/post/24
JVM对象内存分配方式总结 http://viluo.com/post/23 通常来说关于JVM对于对象的内存分配,只要到堆内分配一般就over了,但是在很多人的博客或者帖子中又说了一些其他的分配,比如栈内分配等等,搞的自己有点眼晕,索性就将JVM内存分配的方式统统查了一遍,然后总体上给缕一下。一来是方便自己,权当读书笔记,二来希望有人能一起讨论下。

JVM在内存区域中专门划分出一块区域来,用于存储对象的相关数据,这块区域就叫做堆。堆内的对象数据是各个线程所共享的,所以当再堆内创建新的对象时,就要进行锁操作。而众所周知锁操作是比较耗费性能的,因此针对每个线程,JVM给它在堆上分配了一块“自留地”——TLAB。TLAB全称是ThreadLocal Allocation Buffer,处于堆内存的年轻区,也就是Eden这个区域里。每个线程在创建新的对象时,会首先尝试在自己的TLAB里进行分配,如果成功就返回,失败了再到共享的Eden区里去申请空间。在自己的TLAB区域创建对象失败一般有两个原因:一个是对象太大,第二个是自己的TLAB区剩余空间不够。

这里就涉及到TLAB区域大小的问题了。通常默认的TLAB区域大小是Eden区域的1%,当然也可以手工进行调整,对应的JVM参数是-XX:TLABWasteTargetPercent。

JVM在进行了上面的优化之后,发现创建对象还有可以优化的空间。空间在哪里?在于对象的生存周期。大部分所创建的对象都无法逃脱一次GC的,其中很多对象更是在一个线程、乃至一个方法调用结束后就over了。针对那些只在一次方法调用内生存的对象,JVM通过server方式的优化对其分配策略进行了改进。首先server方式的优化是可以进行复杂的逃逸分析,而后JVM根据逃逸分析的结果,将未逃逸的对象,直接在栈内分配内存空间。什么?栈内??没错!!这个分配方式就是栈内分配,当线程结束时,栈空间被收回,对象也就直接被回收了。由于栈的操作非常快,所以这种对于对象的操作也更加快速。那么栈内分配时对象数据放在哪里?这块我还没看到相关资料,我分析栈帧有三个区域,局部变量区、操作数区和剩余区域,所以对象数据应该是放在剩余的区域里,当然这块是猜测。

以上就是两种额外的JVM内存分配方式。总结起来就是:在server端优化下,如果对象未逃逸,则直接在栈内分配;逃逸之后先尝试在TLAB中分配,失败后再在堆内分配,还失败的话,那么就GC吧。

]]>
Fri, 31 Aug 2012 07:35:42 +0000 viLuo http://viluo.com/post/23
Java编程中写出好代码的建议 http://viluo.com/post/22 最近在做应用的性能优化,在review代码的过程中积累了一些规则和经验。做到这些规则的目的很简单,就是写出“优美”的代码来。

1、注释尽可能全面

对于方法的注释应该包含详细的入参和结果说明,有异常抛出的情况也要详细叙述;类的注释应该包含类的功能说明、作者和修改者。

2、多次使用的相同变量最好归纳成常量

多处使用的相同值的变量应该尽量归纳为一个常量,方便日后的维护。

3、尽量少的在循环中执行方法调用

尽量在循环中少做一些可避免的方法调用,这样可以节省方法栈的创建。例如:

for(int i=0;i<list.size();i++){
System.out.println(i);
}

可以修改为:

for(int i=0,size=list.size();i<size;i++){
System.out.println(i);
}

4、常量的定义可以放到接口中

在Java中,接口里只允许存在常量,因此把常量放到接口中声明就可以省去publicstatic final这几个关键词。

5、ArrayList和LinkedList的选择

这个问题比较常见。通常程序员最好能够对list的使用场景做出评估,然后根据特性作出选择。ArrayList底层是使用数组实现的,因此随机读取数据会比LinkedList快很多,而LinkedList是使用链表实现的,新增和删除数据的速度比ArrayList快不少。

6、String,StringBuffer和StringBuilder

这个问题也比较常见。在进行字符串拼接处理的时候,String通常会产生多个对象,而且将多个值缓存到常量池中。例如:

String a="a";
String b="b";
a=a+b;

这种情况下jvm会产生"a","b","ab"三个对象。而且字符串拼接的性能也很低。因此通常需要做字符串处理的时候尽量采用StringBuffer和StringBuilder来。

7、包装类和基本类型的选择

在代码中,如果可以使用基本数据类型来做局部变量类型的话尽量使用基本数据类型,因为基本类型的变量是存放在栈中的,包装类的变量是在堆中,栈的操作速度比堆快很多。

8、尽早的将不再使用的变量引用赋给null

这样做可以帮助jvm更快的进行内存回收。当然很多人其实对这种做法并不感冒。

9、在finally块中对资源进行释放

典型的场景是使用io流的时候,不论是否出现异常最后都应该在finally中对流进行关闭。

10、在HashMap中使用一个Object作为key时要注意如何区分Object是否相同

在jdk的HashMap实现中,判断两个Object类型的key是否相同的标准是hashcode是否相同和equals方法的返回值。如果业务上需要对两个数据相同的内存对象当作不同的key存储到hashmap中就要对hashcode和equals方法进行覆盖。

]]>
Thu, 30 Aug 2012 07:08:07 +0000 viLuo http://viluo.com/post/22
普通程序员、文艺程序员和2B程序员 http://viluo.com/post/21 本文纯属扯淡,欢迎对号入座。如果你不知道什么是普通青年、文艺青年和2B青年,请绕道。我说的是程序员的版本。

笔记本电脑:

普通程序员:

普通程序员

文艺程序员:

文艺程序员

2B程序员:

2B程序员

求2的32次方:

普通程序员:

System.out.println(Math.pow(2,32));

文艺程序员:

System.out.println(1L<<32);

2B程序员:

System.out.println(2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2);

类属性定义:

普通程序员:

public static int TYPE = 1;

文艺程序员:

/**
* 此策略下消息类型为全局类型,用以通知所有符合B05协议要求的处理者
* …… ……
*/
public static int TYPE = MsgTypes.TYPE_GLOBAL;

2B程序员:

public static String TYPE_ONE = "1";
public String TYPE = TYPE_ONE;

手机开发平台:

普通程序员:J2ME/WinCE/BlackBerry/Symbian/Android

文艺程序员:iPhone

2B程序员:MTK

桌面应用界面开发:

普通程序员:Swing/MFC/C#

文艺程序员:WPF

2B程序员:VB(哈哈,不要怪我,我也做过VB项目,我没有偏见,不过关于VB的非议实在是太多太多了,连Dijkstra都说,“It is practically impossible to teach good programming to students that have had a prior exposure to BASIC: as potential programmers they are mentally mutilated beyond hope of regeneration.”)

即时通讯软件:

普通程序员:工作:MSN,生活:Skype

文艺程序员:工作:GTalk、生活:保密

2B程序员:工作:QQ,生活:QQ

问题查询:

普通程序员:Google搜索

文艺程序员:GoogleScholar/Scirus

2B程序员:百度知道

Window死机的解决方案:

普通程序员:根据异常信息搜索问题原因和解决方法

文艺程序员:哥从懂事起就不用Windows的!

2B程序员:重装系统(万能解决方案),安装360

个人Blog:

普通程序员:CSDN/CNBlogs/BlogBus

文艺程序员:个人站(俺不是自夸啊,哈哈)

2B程序员:QQ空间

笔试地点:

普通程序员:会议室/教室

文艺程序员:咖啡厅

2B程序员:食堂

Java面试题目:

普通程序员:请分别说一说封装、继承和多态的含义?

文艺程序员:如果要设计一个系统,用来统计世界上所有道路的总数,你会怎样设计?

2B程序员:请说说Spring2.5.6版本和2.5.5版本的区别?

仅此一笑,你还有什么更有意思的程序员版本?

]]>
Thu, 30 Aug 2012 06:33:34 +0000 viLuo http://viluo.com/post/21
理解RESTful架构 http://viluo.com/post/20 越来越多的人开始意识到,网站即软件,而且是一种新型的软件。

这种"互联网软件"采用客户端/服务器模式,建立在分布式体系上,通过互联网通信,具有高延时(high latency)、高并发等特点。

网站开发,完全可以采用软件开发的模式。但是传统上,软件和网络是两个不同的领域,很少有交集;软件开发主要针对单机环境,网络则主要研究系统之间的通信。互联网的兴起,使得这两个领域开始融合,现在我们必须考虑,如何开发在互联网环境中使用的软件。

RESTful架构,就是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。

但是,到底什么是RESTful架构,并不是一个容易说清楚的问题。下面,我就谈谈我理解的RESTful架构。

一、起源

REST这个词,是Roy Thomas Fielding在他2000年的博士论文中提出的。

Fielding是一个非常重要的人,他是HTTP协议(1.0版和1.1版)的主要设计者、Apache服务器软件的作者之一、Apache基金会的第一任主席。所以,他的这篇论文一经发表,就引起了关注,并且立即对互联网开发产生了深远的影响。

他这样介绍论文的写作目的:

"本文研究计算机科学两大前沿----软件和网络----的交叉点。长期以来,软件研究主要关注软件设计的分类、设计方法的演化,很少客观地评估不同的设计选择对系统行为的影响。而相反地,网络研究主要关注系统之间通信行为的细节、如何改进特定通信机制的表现,常常忽视了一个事实,那就是改变应用程序的互动风格比改变互动协议,对整体表现有更大的影响。我这篇文章的写作目的,就是想在符合架构原理的前提下,理解和评估以网络为基础的应用软件的架构设计,得到一个功能强、性能好、适宜通信的架构。"

(This dissertation explores a junction on the frontiers of two research disciplines in computer science: software and networking. Software research has long been concerned with the categorization of software designs and the development of design methodologies, but has rarely been able to objectively evaluate the impact of various design choices on system behavior. Networking research, in contrast, is focused on the details of generic communication behavior between systems and improving the performance of particular communication techniques, often ignoring the fact that changing the interaction style of an application can have more impact on performance than the communication protocols used for that interaction. My work is motivated by the desire to understand and evaluate the architectural design of network-based application software through principled use of architectural constraints, thereby obtaining the functional, performance, and social properties desired of an architecture. )

二、名称

Fielding将他对互联网软件的架构原则,定名为REST,即Representational State Transfer的缩写。我对这个词组的翻译是"表现层状态转化"。

如果一个架构符合REST原则,就称它为RESTful架构。

要理解RESTful架构,最好的方法就是去理解RepresentationalState Transfer这个词组到底是什么意思,它的每一个词代表了什么涵义。如果你把这个名称搞懂了,也就不难体会REST是一种什么样的设计。

三、资源(Resources)

REST的名称"表现层状态转化"中,省略了主语。"表现层"其实指的是"资源"(Resources)的"表现层"。

所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。你可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符。

所谓"上网",就是与互联网上一系列的"资源"互动,调用它的URI。

四、表现层(Representation)

"资源"是一种信息实体,它可以有多种外在表现形式。我们把"资源"具体呈现出来的形式,叫做它的"表现层"(Representation)。

比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,甚至可以采用二进制格式;图片可以用JPG格式表现,也可以用PNG格式表现。

URI只代表资源的实体,不代表它的形式。严格地说,有些网址最后的".html"后缀名是不必要的,因为这个后缀名表示格式,属于"表现层"范畴,而URI应该只代表"资源"的位置。它的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对"表现层"的描述。

五、状态转化(StateTransfer)

访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。

互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(StateTransfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。

客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。

六、综述

综合上面的解释,我们总结一下什么是RESTful架构:

(1)每一个URI代表一种资源;

(2)客户端和服务器之间,传递这种资源的某种表现层;

(3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。

七、误区

RESTful架构有一些典型的设计误区。

最常见的一种设计错误,就是URI包含动词。因为"资源"表示一种实体,所以应该是名词,URI不应该有动词,动词应该放在HTTP协议中。

举例来说,某个URI是/posts/show/1,其中show是动词,这个URI就设计错了,正确的写法应该是/posts/1,然后用GET方法表示show。

如果某些动作是HTTP动词表示不了的,你就应该把动作做成一种资源。比如网上汇款,从账户1向账户2汇款500元,错误的URI是:

POST /accounts/1/transfer/500/to/2

正确的写法是把动词transfer改成名词transaction,资源不能是动词,但是可以是一种服务:

POST /transaction HTTP/1.1

Host: 127.0.0.1

from=1&to=2&amount=500.00

另一个设计误区,就是在URI中加入版本号:

http://www.example.com/app/1.0/foo

http://www.example.com/app/1.1/foo

http://www.example.com/app/2.0/foo

因为不同的版本,可以理解成同一种资源的不同表现形式,所以应该采用同一个URI。版本号可以在HTTP请求头信息的Accept字段中进行区分:

Accept: vnd.example-com.foo+json; version=1.0

Accept: vnd.example-com.foo+json; version=1.1

Accept: vnd.example-com.foo+json; version=2.0

原文链接:理解RESTful架构

]]>
Thu, 30 Aug 2012 03:43:43 +0000 viLuo http://viluo.com/post/20
谈谈对程序员的培养 http://viluo.com/post/19 这篇文字是我好久以来的想法,有一些感悟,有一些激烈的言辞,我很自豪我就是一名程序员,我希望给程序员或者前程序员们带来一点启发。也许你认可我的言辞,也许你不屑我的观点,无论如何,欢迎谈谈你的看法。

让程序员做更多种类的事

为什么有人说小公司锻炼人?在小公司,条件并不那么齐备,很多事情都需要程序员自己做,自己去澄清需求、自己做设计、自己搭建环境、自己测试,甚至自己上线、自己维护(这件事情在我们团队被称为“自己吃自己狗食”)。

然而到了某些公司,在一些正规的流程下,“人”反而显得不那么重要了:一个完、整的流程被切分成若干环节,程序员只能负责那小小的一环,更严重的是,他们显示出消极和片面的情绪,他们称自己码农,自嘲并且觉得在总做一些低级和毫无技术含量的事情。

关于“码农”——这似乎是一件很悲哀的事情,在美国,程序员是一个相当值得尊敬的行业,去年硅谷的技术员工年薪平均已经超过10万美元;在中国,教师的行业起码获得了人们口头上的尊敬(虽然实质上也许并没有获得应有的待遇),而程序员,往往只能看到自己给自己呐喊。

这里没有绝对的对错之分,但我一直在博客中强调软件开发实质是一种创造性的劳动,只是在一个流程严格控制的公司内,这样的创造的火花可能被扼杀,换来的好处是人员的流动中,代价最小化:谁都可以走、谁都可以代替谁,所有问题都被认为只是时间问题……那么,程序员,你的自我价值何在呢?

关于测试

我听到很多真实的声音,都在抱怨测试工作乏味、加班辛苦。我认为,国内大部分的测试,其实完全是程序员扭曲的工作形态导致的。

专职的测试人员去做专项测试、整个系统的集成测试等等,我都能理解,但是绝对不应当深入模块内部去测试开发人员的代码,绝对不应该去考虑所谓的代码覆盖率——因为这些,比如白盒测试,本都是程序员自己该完成的事情。程序员自己开发的程序,当然要自己验证功能,很多软件公司在一些项目流程里就是没有专职测试介入测试执行,原因很简单,难道自己拉了稀,让别人给你擦屁股?

另一方面,测试该掌握哪些技能?

我见过许多测试人员,他们抱怨工作总是被轻视,没有说话的分量,但是每天的工作就是了解开发的流程,根据流程完成用例的编写,然后执行那些用例,发现bug并填写bugreport。如果一名测试只需要做这些事情,那么确实太没有技术含量了、太没有入门门槛了,太单调乏味了。

测试需要成长,既为自己考虑,也为团队中的程序员考虑。

做一名优秀的测试,除了要有分析能力、辨识能力等等软实力以外,基础技能如通信协议、脚本、编译原理、操作系统等等,亦视情况掌握,只有这样,才能和程序员从并不清晰的需求开始,一起去分析、设计、实现和质量保证、去和上上下下的环节做沟通、论证。微软的测试(SDET)都把算法和编码视为基础要求,敢问国内一些公司的测试人员,有几个敢这么说?

让程序员远离浮躁

去看看国内几家知名博客和论坛,多少浮躁的博文和帖子四处乱飞?多少人在骂公司狗血,在自嘲薪水,甚至在人身攻击?

浮躁的心态可能是造成这种现象的罪魁祸首,有一位同事说得好,很多具体的技术都可以学,但是执着的心和对技术的热爱是不容易学得的。

整个社会都在日益浮躁,ITEye本来也是一个有技术深度、氛围浓厚的网站,现在看起来有些鱼龙混杂;微博的流行正验证了社会浮躁的趋势,很多人没有时间和耐心去写完整的文章,但是两三句感慨、甚至一词半句的惊叹却容易得多;程序员像寻找速配男女一样,在这个概念翻飞的时代,茫然地寻找自己应该进入的领域,应该了解的技术,于是,很容易看到许多所谓的架构师和咨询师可以把这些东西讲得海阔天空、天花乱坠,这个概念、那个素语,但是——给他一个具体问题吧,要他写写实际代码吧,你会要了他的命。

给程序员一点宽松和鼓励

都在说国内的IT环境不如欧美,大量劳动力的富余让人力成本变得无比廉价,能让青年付出卖肾代价以购买大部分配件都“MadeIn China”的iPhone,中国却只获得了1.8%的利润,这些都让人感叹,为什么中国做不出苹果系列这样的产品?为什么中国始终要给别人套上“廉价劳动力”的帽子?中国的程序员,你们的创造力呢?你们精彩的点子呢?

好吧,我听到有人在说是教育体制的问题。对于程序员的地位的思考有很多,可惜我们不是教育工作者,我们也没有那么大的影响力,不过,在团队中间,我们应当多给予这些脆弱的程序员们一点鼓励,让他们表达出自己的想法,特别是在设计上、实现上的想法,不要嘲笑任何人弱智,不要咒骂评审的代码狗屁,多一点沟通,把眼光放长远一些。

这些能表达出想法的人起码可以证明两点:有思考、有勇气,仅这两点,仅足以让旁观的人鼓掌。

一名优秀的TeamLeader,不应该整天陶醉在跟踪项目进度和获取程序员工作反馈上。我们都明白,所谓一张一弛,在工作压力恰当的时候,是最适宜工作的,程序员的工作本来就需要创造性,没有适度宽松的环境,就没有思维的火花。

让程序员自己去布置工作台,给他们创造机会聊聊天,把各个团队、各个角色放到一起说说话,组织分享一些有趣的话题,做一些有意思的研究(最初程序员都很腼腆,得劳烦您多牵线搭桥了),TeamLeader的工作,绝不仅仅是说“行”或“不行”。

程序员的理想

1983年,乔布斯对百事可乐的CEOJohn Sculley说:“Do you want to sell sugar water for the rest of your life, or do you want to change the world?”(你想卖一辈子糖水,还是改变世界?)。

我们已经不在战火纷飞的年代,有多少程序员还愿意怀抱那些原始的理想,还能说出“做互联网的弄潮儿”、“用技术改变世界”这样的话来?

这个世界执着的人并不多,用这样的理念去吸引程序员的公司,则更少。

关于技术

我始终觉得,做软件的公司是需要依赖于技术的,当然国内可以称得上是“技术公司”的并不多,但是,优秀的技术能让程序员脱颖而出,也作为一种谋生的手段,为其觅得不错的回报。

做技术能不能一直有饭吃?我想答案是肯定的,有许多公司声称技术路线可以一直有发展的可行性,但是技术牛人还是转了管理或者一走了之,有一种粗暴但是易行的鉴别技术公司的办法,就是寻找里面又没有40岁,甚至50岁以上还在做实际设计和编码的人(不是那些脱离了具体实现向客户吹嘘的角色)。

世界上有两种最赚钱的人,一种是真正的生产者,他们用自己独一无二的聪慧、灵感和劳动创造了价值;一种是极其老道的投机者,专门从别人口袋里把钱掏出来放到自己口袋里。他们都受人尊敬、受人瞩目,后一种让自己的生活变得更美好,但是前一种,让整个世界更美好。

如果你不能在技术的道路上坚决地走下去,在大部分公司做市场、做管理、做咨询,顺便鄙视一下别的辛苦工作的程序员,那么也可以混得很好——毕竟,生活的解总是有很多种。

原文链接:谈谈对程序员的培养

]]>
Thu, 30 Aug 2012 01:27:35 +0000 viLuo http://viluo.com/post/19
技术人员,告诉你我看到和经历的拯救路程----创业的故事 http://viluo.compost/18 在前面,我们已经发布了两篇文章,一篇是温水煮青蛙,还有一篇是一个人牛人的故事,分别讲了不同IT人的不同选择和命运,今天我就和大家一起来说说有关一些创业的事情。

还是那句话,文章中的观点和故事仅供参考,如果不认同,那么请将之作为小说阅读,免得过分的纠结。

其实,很多的朋友就问我了:你怎么有这么多的故事?你怎么认识这么多的人?

简单的回答就是:接触的人多了,看的多了,想的多了,自然而然的故事和感触就多了。

一直以来,我个人都比较的关注人脉这方面,从参加工作开始,就很注意这一点,于是很多同事的朋友,朋友的同事,同事的同事等等,这样的二度,三度人脉就慢慢的发展成为了一度人脉,以此类推下去,就看到了很多不同的故事,有的是和IT相关的,有的是其他行业的。

好吧,言归正传,我们来讲讲几个IT创业故事。

看到创业,很多人会有很多的话题,很多人会说,这是自己一直以来的理想,希望有一天可以实现,于是乎开始滔滔不绝。

但是,林子大了,什么鸟都有。接触的多了,发现什么样的创业者都有。不同的创业者给你很多不同的反思和体会。

故事一:切莫“雷声大,雨点小”

依然记得我接触到的第一个创业团队,就称之为H吧,那是我的第一个公司,在公司被的一伙人看中,找个机会和我聊天,和我聊了很多,例如我平时干什么,周末在家里一般做什么事情,喜不喜欢技术等等,绕了很大的弯子,最后告诉我他们是个创业团队,问我想不想加入他们。那是我生平第一次听到和接触“创业团队”。我表示没有兴趣,因为对创业没有任何的概念,没有任何的目标,一下子把“创业”推到我面前,有点不知所措。

因为是同事的关系,所以接触的比较多,而且很多的事情都开始明白。原来团队已经成立了3年,而且团队的创始人一个人已经把一个产品做了5年,他们这批人是后来加入的,这5年以来一直在不断的完善着这个产品,要成为一个很好很强大的东西。

我当时被震住了:做了5年啊,好厉害。看起来是一个很牛的东西。团队H也曾经想过出来做,创业人也曾经一个人出来过,但是最后还是回到公司上班。后来H团队告诉我,他们想去募集创业基金,这样就可以正式的开动起来了。这个基金的事情后来就不了了之。其实,我后面明白到,造钱的能力很重要,没有钱,而想办法搞到钱,搞资源,是活下去必要的技能。因为不能总是指望别人给你送钱来,万一资金链断裂怎么办?还能活吗?起码先得有点盈利,解决生存问题。

到今天为止,H团队依然存在,也有自己挂牌的公司,依然有自己的产品,但是一直没有真正的搞起来。团队的各个成员依然在公司里面上班,而且公司安逸的环境,似乎已经使得大家不怎么想出来。

团队H一直没有决心走出来,曾经创业人一个人出来之后,一个人在默默的做着自己的产品,最后还是回去。没有做事业的那个狠心和决心。

很多团队说是在创业,其实很多的时候,是一个心理的安慰,虽然这样说不太正确。往往由于一些压力,或者说因为理想,或者一些启发,想做成一些事情,但是实现这个理想的决心不大,渴望不强,成立之时,雄心勃勃,但是热情持续时间不长。

团队H其实就是一个非常单纯的团队,里面的人员都是纯的技术人员,一直在默默的做着自己的东西,也认为这个东西比较牛,因为也曾经看到类似的产品成功,于是认为这就是市场,这就是希望。其实说到底还是在闭门造车,没有真正的经历过创业历练,一直还处在温室和假想之中。

这里不是说鼓励大家随便出来搞,也不是鼓励大家出来冒风险,而是说,既然做事情,那就得有个做事情的架势和态度。就的有“卧薪尝胆,三千越甲可吞吴;破釜沉舟,百二秦关终属楚”的魄力。

凡事先谋而后动,而不是一直在“谋”,动不起来,导致最后“雷声大,雨点小”。

后来,团队H告诉我,他们的产品可以演化成为XXX产品,而且这个很有市场。去年,我去北京的时候,有朋友告诉我一个事情:真是想不到,那个做XXX的公司,居然一年之内搞上市了。我心里一惊:同样的东西,不同的人搞,结果不一样,H团队的产品有10年来,何时起来。。虽然很多的时候,很多的团队起步比人家早,但是依然可以后来者居上。创业中没有先来后到的问题,只有成王败寇的事实。

故事二:需要的是战友

这个故事依然发生在我的身上。

现在在回想起来,没有了之前的义愤填膺,比较的淡定了。曾经我以为创业是个很简单的事情,找几个人,接几个项目干起来就行了。结果用赵本山的一句话就是“死的老惨的”。

干了几年,感觉自己有点小本事了,有点关系了,于是到处好说歹说的拉了几个人干起来。自己一个人出来干,其余的人都窝在自己的公司,因为出来风险太大,而且现在也可以看到任何的好的势头。

我同意了。表示很理解。只要大家把分配的事情搞完,拿自己该拿的钱就行了。虽然说是同事,但是其实最后还是因为钱的关系,大家走在一起。

这个事情,让我的思想彻底的发生了变化,改变了我的一生。因为大家都是技术人员,出来创业,接项目,那么总的有人带头了。技术人员很多的时候都不愿意出世,都喜欢呆在自己的小圈子里面。

于是,我一人开始到处的谈业务,拉项目。整天在外面不断的奔波。每次当我想退缩的时候,我告诉自己:必须上,因为没有后路。

我也曾经是技术人员,本来普通话就不是很标准,假设谈判能力也很烂,交际能力也不行,但是一切还是硬着头皮上,因为我知道,我不做,就没有人做了。第一个吃螃蟹的人只要吃点苦头的。

接到项目,我一个人顶着几十万的风险签约。我后面的所谓的团队是不会和我冒这个风险的,但是钱还是平分的,你不能不给,否则人家不干。

我记得看过这么一段话:很多的人都有价值的底线,有的高,有的低。有的人只要把钱谈到1万,就可以看出他是什么人;有的人,把钱谈到10万,才可以看出他们是什么人;有的人….

项目到手了,钱也到了,大家就干起来。这是一个比较费心的事情,费心不是因为项目,而是因为一起做事情的人。自己签了约,冒着大的风险,心里自然是火急火燎的。但是其他人不同,人家就是来搞外快的,虽然口头上说的是关系好,哥们有前途。真是映着那句话“操买白粉的心,拿赚面粉的钱”。

每次分完任务,心里就担心任务进度,因为今天有人告诉你家里有点事情不能搞;明天有人告诉你每天的加班身体不舒服。最后下来,等到进度报告的时候,结果和自己估算的一样:还是靠自己。于是,一个人挽起柚子,开始加班加点,把分配给别人的任务自己搞定。

曾经以为创业是个很容易的事情,以为接到几个项目就可以飞黄腾达,也曾经在接到项目的时候搞到创业也不是那么难,完全没有网上和别人口中说的那么艰辛。

生活是很公平的:没有随随便便的成功,没有一蹴而就的辉煌。你曾经因为走的太顺,没有吃过的苦,生活在后面会给你一一的补上来。越早吃苦越好,醒悟的越早,成本和代价也就越小。这和我们做软件一样,bug越迟发现,代价就越大。

通过这个事情,看清了很多的人,也明白了很多的时候,让我深刻感受的到的一点就是:团队。一个真正的团队的重要性。你要的是战友,是兄弟,而不是那些浮云。

一个人能力再强,但是毕竟能力有限,而且中途不能出任何的意外。后来,陆陆续续的有很多的朋友介绍项目,也有一些大的,但是我都没有接了。一个因为钱而走在一起的团队,就好比夫妻同床异梦,迟早要散的。或者现在项目小,风险看不到,一旦项目大了,这样的团队产生出来的问题会更大。

所以,如果你有一群志同道合的兄弟,战友,你就已经有了无价的财富。剩下就是和这些东西把这些无形的无价的财富有形化。真心的珍惜战友,珍惜友情,珍惜团队中的每一个人!

故事三:看市场,重推广,不要闭门造车

这个故事来自我认识的一个团队,称之为S吧。S团队的技术能力很强,而且他们在自己所处的业务行业也是做了上十年,技术锻炼和业务经验上面可以说都比较的成熟了。

团队S原本是一个公司的核心团队,做了很长时间之后,而且有些名气。所以决定出来自己发展。大家为了一个共同的目标开始奋斗。

技术人员都有追求完美的心里,总是希望把一个东西做的尽善尽美,每天也是乐此不疲的不断的修改和完善自己的软件产品。于是团队S就开始不断的升级自己的软件,从1.0到2.0。版本是在不断的升级,其实还没有真正的卖出一套,也没有几个说的上来的客户。

我们都清楚,很多的软件做出来是可以的,但是能不能卖出去,那就是另外一回事了。S团队花了很大的气力,做成了不错的东西。一堆人就开始苦闷了:没有任何盈利,一帮子人是要养家糊口的,大家已经是卯足了劲在干,狠下心在做事情,背着自己的家人出来创业,信誓旦旦的承诺会活得比上班好。

现实是残酷的。而且在国内,没有几个人机构或者客户敢买一个不太之名的团队做出来的软件(在IT界中你出名,但是不确保你在那个行业中出名,这就是跨行的问题了),尤其是这个软件的行业对安全要求非常的高。并且很多的客户也是心底认为软件是不值钱的,宁愿砸钱买N多的服务器,因为毕竟这些看的着,摸得着,心里踏实。再说了,就算买,也会选择国内知名的大企业买或者定制。

团队S一筹莫展,原先的激情不断的被现实泼冷水。东西是好东西,你认为是好东西,别人不一定这么认为。S团队也是发动了自己的人脉,找自己认识所有的人,以及二度,三度,甚至N人脉,结果依然甚微。

遇到这样的情况,很多的团队成员就产生离意的,自己不说,家里人会唠叨的。

后来S团队终于接到了项目,不过,再次的项目不是按照他们期望进行的。因为客户不要他们的全部功能,只要一部分,而且还要重新开发其他的一些功能。“及时雨”的到来,让S团队的人想都没有,答应了。就算其他几个功能没有,凭借自己的团队实力也是可以做出来的。

这个项目让S团队日夜的加班加点,因为时间越短,他们的成本就越小,就可以去接更多的其他的项目。

项目中有结束了,解决了个温饱。同时从这次的项目来看,S团队意识到自己的产品功能不足,于是鼓励大家开发下一个新的强悍版本。

于是大家有了新的干劲,火力全开,开发版本3.0。大家有了新的希望,认为这个软件搞完之后会更好的发展。

等待3.0开发完整之后,处境依旧。大家任然是没有过的紧巴巴的。都不拿工资,扛着。项目没有来不能闲着,于是4.0,5.0,6.0不断的开发。

后来这个团队就解散了。

团队S最大的原因就是大家太“技术化”了,没有市场的思维和推广的方法,没有跳出技术人员处于的思维定势。

这就好比后人总结从古至今2000年以来,为什么很多的农民起义都以失败告终,就是因为起义的人没有彻底的摆脱农民的思维,说到底还是农民阶级。为什么新中国成功了,因为是工人阶级这个先进的思想在引导。我们暂且不说什么阶级不阶级的问题。有朋友可能会说:“你这不是事后诸葛吗”,在别人的事上面,我们还是“事后诸葛”,那么引以为鉴,在自己的事情上面,就要“事前诸葛”。

S团队的情况类似,一个团队中初期,起码要有一个人开始懂市场,运营,推广。很多的时候需要一部分的人放弃一些东西,例如自己喜欢的技术,此消彼长,以市场为导向,先活下来。

这其实这个道理在娱乐界用的很多。很多的明星出道的时候,都是以组合出名,之后大家成名之后就单飞了。因为以组合存在,不同的人有不同的人群喜欢,“萝卜白菜,各有所爱”,如果组合中有4个人,只要有一些人喜欢组合中的一个,那么这个组合就有25%机会活下来,即使其他3个人不受喜欢。但是如何这四个人都是单个的,那么就死的老惨。

所以,团队不要“偏科”。

故事四:坚持活下去

很多的IT朋友开始选择了创业,特别是在现在,很多的朋友认为新的浪潮已经到了,第二波互联网开始了,于是纷纷的跳下去了。很多的大的公司也在不断的调整,因为害怕这场“革命”会终结他们。一时间,国内的创业热情暴涨。

很多的朋友创业都是拿着自己多年的积蓄开始的。当然,很多的朋友也想到去找VC,去找合资人,但是在开始的时候,这些是没有的。特别是你没有任何牛X的背景,没有吓死人的经历和光环。

有朋友,都成立了创业团队,也作出了很不错的产品,也有很多的人在使用,但是没有任何的盈利。朋友说,现在是积累用户的阶段,等到用户积累到了一定的时候,就可以收费了,就像QQ一样,开始搞免费,后续就卖QQ币了,所以现在要扛下去。

也有朋友告诉我,其实很多的用户根本就不知道他们用的软件,玩的游戏是哪个公司搞的,而用户只知道一点:免费的我就要,收费的我不要了。

所以到现在为止,很多的朋友都是不断的烧着自己的积蓄,而且没有新的资金进来,大家都在扛,不断的扛着,等待奇迹出现的那一天。但是奇迹什么时候出现,说都不知道,但是谁都知道:在国内创业,就要比谁可以抗到最后。

也有朋友想走国外市场,因为欧美那边是收费的。Agree Bird 的神话让大家对国外的收费模式前景看好。于是也做了一些产品,花了大量的时间,但是老外似乎不是很买账,下载量很少,甚至有的产品无人问津。朋友知道,“万事开头难”,继续扛下去。

愁发展,找出路,没有经历过创业的朋友可能体会的不是太深刻。看着账户里面钱的数字不断的递减,看着跟着你的兄弟一个个累的要死要活,无法停下,看着你的家人勒紧腰带还安慰你,那种心情,让你每天睡不着,吃不下,每天脑子在高速的旋转,每天半夜突然惊醒一生冷汗。

每次谈到这里,心里特别的难受,很难再讲下去。就到这里吧,创业的,朋友们,加油。

原文链接:技术人员,告诉你我看到和经历的拯救路程

]]>
Wed, 29 Aug 2012 09:37:57 +0000 viLuo http://viluo.compost/18
技术人员,你拿什么来拯救你的生活----一个牛人的故事 http://viluo.compost/17 不知道大家对牛人是如何定义,但是不管如何,我依然认为做个朋友是个牛人。朋友是比我大,算是我的前辈,入行近15年了。我认识他是因为曾经我们共事过一段时间。共事的时间不算太短,也不算太长,后来朋友就走了。

还是像前面一样,我成他为D吧。

我们都知道,技术人员的圈子很小的,朋友也就那几个,所以,一有空闲的时间,大家就会相约出来聊聊天,谈些人生,吐吐槽。D是做Java出生的,做了9年,后来转了管理,朋友在一家500强的外企做管理,也算是不错了。而且从进入IT到现在,一路也算是“辉煌”。

标注:这里提到朋友做java,我没有潜台词说做.NET的不好,大家不要以此为话题展开口舌之争。

不用说,在别人眼中,肯定是很羡慕的。正所谓“家家有本难念的经”,其实从和D接触的经历来看,D其实也是心里有些感触的。

D有一天吃饭完之后,问了我一个问题:你猜猜,我当初为什么不搞技术了?

我苦笑了一下,没有说话,不是说答不上来,而是说,一大堆话在嘴边,不知道如何说。

D也是好像并不在意我是否真的回答,一个人开始说话了,其实他就是想说说话而已。

我曾经以为,把技术做很厉害了,就会很有前途的。这是最让我伤心的。很多的公司不需要太厉害的技术,不需要太厉害的人才,而且也不愿意付高薪。就算有要技术的公司,人家也不一定要你,以你的年纪,这个能力,别人死活不信。

我曾经以为,企业那句话“千金易得,一降难求”是真的,其实这就是一个谎言。企业都是要赚钱的,他们都是要用最少的钱来雇人。打着“以人为本”的口号,打着“不拘一格降人才”的幌子,其实依然是老的一套。

我曾经以为做了架构师,就会有好生活,会做自己喜欢的事情,拿着好的待遇享受人生。我依然错了。在国内的架构师,不管是外企还是国企,架构师就是一个打杂的,一个空有头衔而没有相对应待遇的人。

我曾经认为,只要拼命的工作,努力的表现,就会在公司有好发展,就会有升迁的机会,上面就会赏识和认可你。大错特错,后来发现,企业的高层都是玩“空降”,很少有公司内部的人升到那个职位。也发现,企业玩的就是“一个萝卜一个坑”,你的直属上司没有走,你永远升不了。

以为有能力就可以脱颖而出,依然错,如果比你先进入公司的一批人没有走,机会永远轮不到你。可能你熬个十几年,充其量是个Leader,小主管,但是永不怕不上去,特别是如果你一直搞技术。

我曾经认为,去了很多的大企业就会很好的,其实我错了,其实企业给的各种待遇远远不是你想要的。最后的结果就是:要么你自己搞,要么就退出这个圈子。毕竟,你是打工的,人家想把你怎样就怎样。

我曾经因为创业就是带着几个人关系不错的人,开始接单子,搞项目,赚大钱。后来发现,不是每个人都想要创业,不是每个人都想和你一起奋斗,更加不是每个人都可以成为你的团队,你的兄弟,你的战友。有的人就是为了混点钱,捞点外快,然后走人。还有的人就是为了在你混的不错的时候,蹭你一点,等你处境不行的时候,然后拍屁股走人。还有的就是跟着你学点经验,站点见识,为以后的跳槽做准备。

D一个人说了很多,说的在座有的朋友眼圈红红的。

其实很多的行业都是一样,没有一个特别好的“黄金”产业。很多的时候就是一个围城。

看到这个,可能很多的朋友会觉得非常的迷茫了,其实大家是想知道如何成为一个大牛,但是本篇没有写,没有办法写,成功的方法只有一个,但是失败的理由却是千千万万。就好比很多人要有好的口才,不是看几本《演讲与口才》的书就搞定了,也不是听几次讲座就成了,一切还要按照适合自己的方式去摸索,练习。

大家听我继续说完,心里就有数了。

其实只是D的生活,不代表全部人的生活。只是通过这个事情,告诉大家一些鲜为人知的背后事情,自己要有个心理免疫能力。

我还是觉得鲁迅的一句话说的好:“世界上本没有路,做的人多了,也就成了路”。

但是,反过来一想,一条路走的人太多了,你还能插脚吗?

这让我不禁的想起我们村里人的故事。

十几年前,村里很多的小孩子读完小学,都不读了。开始打工赚钱。种地,肯定是不乐意的。并且很多的小孩子还是有梦想的,不希望像他们的父母一样,一辈子窝在村里。

那时候,村里有人在外地做裁缝,做衣服,而且在大城市,每年还赚个几千。几千啊,十几年前是个大数字。于是很多的十多岁小孩子成为学徒,在外面做裁缝。

一年一年,一批有一批的孩子成为了裁缝。一批有一批的孩子出师,自立门户。后来,越来越多的人加入这个行业,人越多,价格就开始便宜了,以前做一件衣服1元钱,现在只有5毛钱。

确实是有路,但是是别人已经踩烂了,走遍了的路,等你下脚的时候,已经不是那么回事了。

后来村里又有人说去深圳打工好,特区啊,而且还确实有几个人在那里赚到了钱,而且还是工头,混的有模有样。于是大家一窝蜂的又去了。

其实这情况和之前如出一辙,有的人不断的摸索,不断的尝试,第一个吃螃蟹。等到别人把螃蟹吃的差不多的时候,就告诉你,此时,你还能吃几口?

在回头反思我们技术人员的生活。其实也是被外界在不断的忽悠着跑。今天这个技术热,大家一窝蜂的追。明天那个东西火,大家又跳进去了。最后,你得到什么?除了累,还有什么?

其实很多的事情都是相通的,技术也是生活中的一部分,技术圈子和其他圈子一样,有这类似的规律和状况。

再可以这样一想:当一窝人一窝蜂的去一个行业的时候,那么那个行业就对立的行业,可能就空了,市场就有了,机会也来了。

没有一成不变的发财路,没有永恒的成功大道,用自己的脑袋过自己的生活,不断的摸索,不断的上进,才会找出属于自己的路。

通过别人的经历,反思自己的生活,在D这样的朋友在一起,一路上面给了我不少的启发,是我明白了很多的事情。

Thanks,D !

第三个故事,我从形形色色的创业朋友中看到的情况!

原文链接:

]]>
Wed, 29 Aug 2012 09:34:45 +0000 viLuo http://viluo.compost/17
技术人员,你拿什么拯救你的生活----温水煮青蛙 http://viluo.compost/16 有时候,我常常问自己这样一个问题:如何哪天你不搞技术了,你还可以干什么?还能够用什么来养活自己和家人?

很多的朋友看到这个问题之后可以给出很多的答案,例如转管理,自主创业,实在不行,就改行!

下面就来告诉大家几个真实的故事。

朋友已经工作了十几年了,我们这就称他为A吧。A是个老老实实地的人,从毕业之后就一直老老实实,本本分分的上班,在第一个公司呆了6年,在6年之中,一直过的平平淡淡,在技术上面的成就也是一般,在业务上面有点建树。

在这几年中,公司来来往往,换了几批人,A一直没有走,最后终于忍不住了:6年来,一直顶着技术人员的帽子,做着不知所谓的事情,打出的打杂,每年的工资涨幅不到500元,6年下来,工资还不到5K。

此时的A,再也不理会当初老板的承诺:你只要好好的干,以后就那个职位就升你了!老板这样拍着A的肩膀说了一年又一年,A也勤勤恳恳的等了6年。

对于一个技术人员来说,特别是在国内,6年,已经算是有一点的人老珠黄了。A想到跳槽,但是想到自己的能力确实不怎么样,特别是想到自己在这里干了6年,就这样走了有点可惜,心里怎么都不服气这样走了。

人总是求安逸的,不是那么有魄力的人,不会随随便便的追求自己目标。老实的A忍着,终于,公司要转型了,原本就不是一个技术公司,发现计算机软件这一块肥肉不是那么好吃的,终于转型了。A被迫走人了。

老实的A,这些年来还算积累了一些人脉,因为之前的同事很多已经在其他公司干。所以A被同事推荐,去了一个还不错的外企,就冲着6年的经验,就可以说明一些事情了。

在IT行业中,似乎有这样一个潜规则:资深人才就是工作了3~5年的;高级人才就工作了5~8年的;高端人才就是8年以上的。看的就是年限,年纪,能力在于其次。

进入外企的A,日子过也行,因为很多的外企在国内也即是简单的人力外包,不需要太多的技术含量,也不需要高深的技术。

有一天,A告诉我,他快活不下去了。原来A有了小孩,虽然说只是多了一张嘴,但是确实要了他全家人的命。小孩子生下来之后,那就是耗钱的金娃娃。从老婆怀孕到生孩子,前前后后不断的跑医院,每跑一次都是钱,特别是小孩子生了之后,这这那那的毛病,一会儿XXX过高,一会儿XXX不足,搞得全家人不得安宁,特别是钱,一个月的工资,在医院一天就没有了。小孩子的奶粉,吃的,穿的,用的,一个月加起来就是不少钱。A的那点工资,那就是杯水车薪。

A也在努力的赚钱,也在外面搞私活,但是私活不是那么好接的,而且很多的私活都是朋友介绍的,朋友也是杀熟,最后一个项目下来,每天上班下班不停的干,最后整个项目的钱就是2w,而且A拿到的顶多就是5K。其余的要么被朋友用这这那那的理由搞走了,要么就被客户用这这那那的借口不给了。

A很无奈,不断的找出路,不管与IT相关的,还是不相干的,在不断的需找。

A的生活,是很多技术人员的一个缩影,大家有类似的经历,其实现在的A处理这样,没有其他的办法,6年下来,没有积累到太多的资源,能力,人脉,交际方面都没有在注意。等到事情突变的时候,一切都开始茫然。这就是温水煮青蛙一样,等到真正感受到变化的时候,有点迟了。

但是,人生是个充满变数的历程,也不知道,A以后会怎么样,但是希望A和像A一样朋友找到出路。

有朋友可能会说:A不够牛,如果技术很牛的话,也是可以活的不错的。

真的是这样吗?

我们下一篇就来讲一讲一个牛人的故事

原文链接:技术人员,你拿什么拯救你的生活----温水煮青蛙

]]>
Wed, 29 Aug 2012 09:30:43 +0000 viLuo http://viluo.compost/16
到底怎么样才叫看书(下篇)? http://viluo.com/post/15 一、再提笔记

对于《》中所提到的内容,得到了各位博友们的普遍认同我感到很高兴,说实话,从小到大我也就认真作过那几次笔记,但是却都印象深刻,并且对以后的影响也很深远,在走了很多的弯路以后,渐渐才发现这其中的道理来。

正所谓“学而不思则惘”,学的目的当然不是形式上好看,当然,我们从小受应试教育的影响太深,花了太多的时间和精力去学一些根本用不到的东西,这种“形式”上的学确实也是没有办法。但庆幸的是我们都挺过来了,大学的教育毕竟也是自己完成的,自主性提高了很多,但仍然有很多的在校大学生不清楚自己以后的方向是什么,大学四年里都是迷茫的,等到工作了一年两年以后,才突然顿悟——原来我在大学里浪费这么多宝贵的时光!如果你还是在校研究生,你会发现那些工作几年再回去读研的学生中,学习的自主性和目的性有多强,因为他们已经深深的追悔过那段时光,并且看到了未来努力的方向在哪里。这些道理是一直在学校里的人很难体会的,一切都是“失去了才会珍惜”。

读书是一个很费时的过程,一本普通的书300多页很正常,软件类的书里1000多页的大部头的著作也很常见。不同于小说是纯叙事性的不需要太多的理解,计算机的书都是需要用大脑去分析和理解的,所以注定了读这些书的速度都不会很快。比如一本300页的书,你用工作之外的时间一共读了7天,实际上到第七天的时候,你对第一天和第二天所读过的东西已经印象模糊了,这时你会意识到这点并立即返回来再看一遍第一天和第二天的东西吗?很多人不会的,人只会更乐意往前看,直到看到最后一页才长嘘一口气,“终于看完了”!于是将书放下,即使以后的几天里,书就摆在那你也想不起来再拿起来翻几下。又过了7天,上周读的内容已经完全印象模糊了。又过了一个月,只有书名还能记得。。。

所以,我们花了大量的时间读书,却效果不大,读了后面的扔了前面的,一遍一遍在重复同样的过程,整个过程里,唯独让我们理解最深的时候,就是在阅读的那个过程当中,那一瞬间的我们对大脑的记忆力保持高度的自信,偏执地认为,将来不论哪个时间,我都将理解到这个程度!事实上,恰恰相反,这只是一个短期的记忆而已,能只维持几天甚至几小时,就像是硬盘一样,你今天删除了一个文件,第二天作数据恢复仍可以找回来,但两个月以后数据恢复就再也找不回来了,因为新的文件已经覆盖了那个位置,记忆的信息被冲掉了。笔记的作用更像是一块移动硬盘,你可以在你认为这文件有用的时候,把它转移到移动硬盘里,即使本机硬盘上的文件被冲掉,你也可以随时读取移动硬盘里的内容。

你虽然花了时间去读了一本书,但对于这本书可能是你的,也可能不是你的,但如果你对它作了笔记,这本书就百分之百会是你的了。有效读书的过程应该是这样的:

有效读书的过程

读书的目的其实最终目的就是形成自己的理解并长期驻存在大脑中,而笔记这个环节是达到这个最终目的最关键的一步。

二、大学课程之授课方法

大家都经历过大学,我对大学的感悟最大的就是,每一门课学的最明白的时候就是考试之前的那一周。因为有一个考试的期限在那,就逼着自己在那天之前必须得把课本啃完,把内容梳理出来,这大约只需要一周或更少的几天时间,取决于老师哪天通知考试。

后来我就常常这样想,大学的课程如果是先考试后上课是不是效率会高很多呢?大学生都有很强的自学能力,突击考试的能力都非常强,几天时间把一门从未学过的课考个及格应该问题都不大,这就造成了大家平时上课不用功,只等考试突击的号角吹响才进入学习状态。如果先考试呢,给大家定个及格线,不及格的算挂科,那大家就会玩命的自学一遍,把考试过掉,这时已经对课程的内容理解的比较深刻了,接下来再从头到尾上一遍课,这就很少会出现有学生因为听不懂而不去听的情况了,大家都会变得愿意听课,并且理解会更进一步,皆大欢喜!可惜啊,大学四年那些课程的价值都为应付考试而大大缩水了。。

三、我是怎么泡图书馆的

上大学时看到有人说过一句话,"在学校要好好利用图书馆的资源,等你工作以后哪有这么大的图书馆给你用啊!"当时对这句话的印象很深刻,但遗憾的是,那时并不会泡图书馆,也没有那么深得意识到图书馆的资源是多么得难能可贵。很多真相都是工作以后才后知后觉的,这其实说明了一个道理,人的认知永远都是只局限于眼前,几年以后回头想想以前的想法只会感到当时是如此幼稚和缺乏思考。

大家都知道,计算机的书籍一般都比较贵,靠完全买书去提高技术,成本上实在太高。还有一个事实要承认,买来的很多书都想不起来看。在买书时,往往我们都会有一种冲动,并且有一种心理在作祟——我得到了这本书,就学到了这门技术!于是,在这样的冲动之下,花了很多冤枉钱买了很多书成了摆设,而技术却没有半点长进。

所以关于买书,我有两点心得和大家分享:

1、能不买就不买。这不是说鼓励大家不读书,反而是为了更有效的读书。古人早就给我们总结了“书非借不能读”,已经是自己的书往往不想读,自认为书就一直在那,我什么时候想读都能读啊,于是一直没有读。而借来的书,因为有期限要还给人家,你就不得不抽出点时间来把它读完,这样才是真的去读了。并且,如果你是按照上面的流程图中画的那样作了笔记的话,这本书你虽然形式上是还了,但你却真正的拥有了这本书,永远的拥有了。

2、三思而后买。如果这本书你周围借不到,你又想读的话,请在买书之前给自己几天时间的准备。准备什么呢?你应该从各个方面了解一下这书的内容,质量,评价等等,并且能列出这书非买不可的几个理由,计划一下你拿到这书后的阅读计划等等。为什么要作这些准备呢,这其实是在强化你读书的动机,动机越强你读书的动力越大,效果也越好,那这本书才不会白读,更不会白买。我给自己的硬性规定是,从我有买书想法的那天起,往后推一周的时间,如果我还是很想买,那就毫不犹豫的出手了!

3、买了书赶紧看。当你拿到一本新书的时候,这时是你对这本书兴趣最大的时候,也是最冲动的时候,这时候应该尽量的拿出一大块的时间来把这本书一口气看完,这样能最大程度的激活这本书的价值,而如果一旦搁置下来,下次再翻开它就不一定是什么时候了。“一鼓作气,再而衰,三而竭”,读书也是同样的道理。

对我们程序员来说,图书馆仍然是最好的资源,不但全面而且免费,还能既定一个期限督促自己完成学习,一举N得。我是爱泡图书馆的,而且经常是从早泡到晚上关门,有时是去图书馆的书,有时候也只是看自己的书,因为图书馆里环境最能让人心静下来,学习看书的效率都很高。不过,我也常常碰到很多过来看书但是明显是瞎看的读者,这很大程度上还是在浪费时间。举几例:

1、没有看书的重点,从书架上一取就是五六本,甚至十几本,这时的心理好像是“我拿的多,看的多,学的多,知道的就多”,其实这是完全自欺欺人的。《上篇》中提到了,一个现代化的图书馆里,你是不可能把每本书都看过来的,你的生命太有限了。像这些取书的人,最后只能是翻翻这个,再翻翻那个,每一本书都不会看超过五分钟,最后原原本本又抱回来了,白白浪费了生命中的几十分钟,却还感到莫名的充实。。。。 我的方式是只取一本,并且这一本一定是我之前在豆瓣上查看过相关评论的,我认为这本书值得去读才去拿来读的,并且,如果这本书真的是好书,是一定要一口气看完的,绝不中途去抓另一本书来看,这会严重的影响理解的连续性。

2、小情侣一起来看书的,完全是浪费时间,并且也影响他人。因为两个人一起来的就会有一种“说话有底气”的心理,所以坐在那就会不自觉的聊天说话,而不顾周围人的感受,这样的读者我也很不喜欢。

3、到图书馆来玩手机的。这部分人很多,抱一堆书过去,然后拿出手机来上网发短信听歌的,也许是把书当成了一种心理安慰。这种人也是不应该出现在图书馆的。

这其实就是一个动机的问题,动机不纯永远也不可能作到专注。当然,也不能要求人人都有这种专注的性格,也许人家就是来休闲消遣的,没有太多的想法,这也没错。对于我来说,因为图书馆离住处不是很近,需要花一定的时间成本到达那个地方,所以,我会对在那的每一分钟都比较珍惜,都是自己选择的嘛,总得为自己负点责嘛,所以我一般会将手机关机,并且一直坐到图书馆关门才收拾东西往回走。

最后,《到底怎么样才叫看书》的两篇总算写完了,谢谢各位的支持!对文中偏颇的地方,纯属个人见解,请大家批判接受。

原文链接:

]]>
Wed, 29 Aug 2012 09:15:59 +0000 viLuo http://viluo.com/post/15
到底怎么样才叫看书(上篇)? http://viluo.com/post/14 一、引入

看到这个题目的时候你可能会感到有点好笑:“这还用问,看书就是把书看了一遍呗..” 没错,我们从小到大就是这样想的,也是这样看书的,信手拈来一本叫作"书"的东西,从头到尾就开看了!随时年龄的增长,我们的“阅历”也多了起来了,但你是不是也有和我一样的感觉——书是读了不少,真正入脑的,能融入自己思想的内容却少之又少,而且随着年头的增加,你越来越不记得自己读过这些书,更不用提书里到底大概是些什么内容了!这就是我们的长久以来的读书方式。。。 你会不会也这样想,如果我能把我从小到大读过的书都能记下来形成自己的思想,那我现在得多牛啊!是呢,我也常常这样想!于是为了让自己更有思想,就去读更多的书,然后在短短的几年,或者只是几个月,甚至仅仅是几天以后,生活的琐事会渐渐把你关于那些书的缓存挤掉,你的大脑又变成了一块被重新格式化的硬盘,那些书又与你无关了。

说实话,我曾经就是这种情况,并且被其深深的困扰过,花了大量的时间去阅读,但是在脑子里留下的却是星星点点的零碎的内容,于是很快这些零星的内容也会悄悄的跑掉,剩下的只有一个好笑的自欺欺人的解释——这本书我读过。。。。就这样,我骗了自己二十几年,这二十几年里,不管是教科书,还是小人书,不管是专业书,还是小说,真是读了不少书啊!我花了多少时间在这件事上面呢,少说也有二十年吧,可是,要问我对哪本书的内容真的印象最深的,真的很难回答的出来,更别说去吸取书的精华了。估计和我一样有这样困扰的人肯定很多。你是吗?

二、经历了就能理解

我经常会想难道我就真的没有从读书中受益过吗?当然有!“咳,咳。。”我要写条sql去数据库里查一下,数据库里垃圾有点多,估计得花点时间,“select* from lifecycle where benefit from reading ——>execute!" (一分钟。。。二分钟。。。五分钟。。十五分钟过去了,数据终于出来了,垃圾数据把系统性能拖的太慢了,是需要作作调优的时候了),好了,你很幸运,系统没有宕掉,下面这些数据留给你们参考:

1、我自认为英语水平还凑合,几次重要的升学考试和就业面试中,都没有在英语这块栽过跟头,由于后来集中学习日语的原因,英语过了CET-6后就很少碰,这个原因我下面会解释。现在很多学生中为英语发愁的不在少数,往往其他各科成绩都很好偏偏英语不能达标,错失了好的升学机会。我回顾自己从小学英语的经历,总有一个点在那闪光,那是我学习英语的转折点。那时是小学四年级,学校刚刚开设英语课,那时和其他的孩子一样,我的英语成绩不怎么样,很吃力。我老爸对我的学习比较严格,当他发现我的英语不行的时候,他开始跟我急了,那时我也真怕他,当老子就是威武啊~我爸是高中毕业,他那时的外语水平差不多也就是我四五年级的那个水平的,但是他还是选择了硬顶上来!他拿出课本让我逐句的翻译对话,我还大概记得那对话的内容:

A:what'sthis?

B:it's a pen.

A:what's that?

B:it's a pencile.

A:and what's this?

B:it's a pencilebox.

A:can you spell it ?

B:yes.p-e-n-c-i-l-e-b-o-x,pencilebox.

A:thankyou.

B:yourwelcome.

熟悉吧?后来还有很多学者指责我国小学英语教材的落俗套,以致学生跟人对话只会说“what'sthis” “How are you? fine, thank you. and you?” 之类的,这些我们都暂且不谈。就说我那段时间是怎么被老爸蹂躏的吧。。。

当时有好多的对话内容我翻译不出来,于是就被无情的鄙视了,并且死规定,以后每天晚上都要默写课文、单词!我那时是真怕他呀,为了应付这一关我是想出了一堆的作蔽手段,小抄、写手上、写桌面上。。都用过了,每天像过堂一样的接受蹂躏,给我幼小的心灵带来沉重的压力,就这么坚持了有多半年。于是,后面的事情你们都猜到了,我的英语成绩上来了,随便找出来一篇课文来我都能熟悉的背出来,得到了来自各方面的肯定和鼓励,回头想想,这都是对心理体验非常好的正能量,直到今天,我都能对英语有信心。现在回想起来,那些小抄的作用就相当于笔记,让我对英语的印象从陌生变熟悉。

2、高考的经历。对于都经历过高考的人们,这个艰辛的过程我就不再多多描绘了,地球人都知道的。我在这想说的是,在这个备考的过程中对我进步影响最大的一个方面,这也是我高考制胜的法宝。那时我有幸和一名来自山东的转校生(也叫高考移民)同桌,他是我高中时期最好的朋友之一,就是他的学习习惯深深的影响了我,他后来考上了中国公安大学,现在是国家保密机构的人员了,国家机密不多说。他有一个什么习惯呢?作笔记!

你看到这可能在笑,“原来就是作笔记啊,这谁不知道,谁不作笔记啊”,没错,我们从小都会要求作笔记,有时候我们也会假意的去作一些笔记,但很多的时候是东记一笔,西记一笔,记完就忘了,也找不见了。我说的记笔记不是这种情况。

在高考那个大环境下,每天我们都会被大量的习题淹没,多的时候每天要作十几张卷子,那时真是麻木了。我的这个朋友就习惯把每次作完的试卷都从头到尾作个总结,把自己不知道的盲点记在一个本子上,随时翻看。我尝试了这个学习方法后,收效非常大,通过自己的实践也对作笔记有了更深的理解,下面说说我的理解:

(1)作笔记一定要找个厚点的本子,品质要好,但不要过于花哨的,简约的最好。因为你要能预见,当你养成这个好习惯以后,你遇到什么新的东西都想往上面记几笔,如果页数太少了,你又要换个新本,这样就会对你的知识系统产生分隔,你会更偏向于经常翻看新的本子,旧的本子就被你束之高阁起来了,渐渐那些你曾经领悟的东西又会变的模糊。

(2)请勿分门别类的记笔记。拿高考这几门课举例,那时我有很多同学的作法是数学一个本,语文一个本,英语一个本,化学一个本。。。基本上是一门课一个笔记本,分门别类,互不干扰。这样并不是最佳的解决方案。我们俩当时的作法就是所有的内容都记到一个本子上,不分科,新的内容就直接追加到旧的内容的后面,形式上也不要记的太整齐,形式可以多样,圈圈点点,勾勾叉叉,这样更容易在你的大脑里形成再深的印象,我称之为“草稿式笔记”,比条理清晰的“流水式”笔记效果好的多。

(3)经常翻看。记笔记其实是对大脑缓存的一个整理,就好比你存在计算机内存里的内容永远是不稳定的,而你一旦写入硬盘,这个安全性就提高了几个数量级(对于程序员的你,道理不解释),同样的道理,你今天对一个问题理解了,但不表明你两天后返回来还能理解到那个程度,最好的办法就是把这个你理解的问题用自己说话的方式记录下来,并且随时翻看,可以说,只要你的这个本子不丢,你就永远能保持对这个问题的新鲜的理解。以至后来,每每月考、期中、期末、摸底的所有考试中,我都只需要拿出笔记本来从头到尾翻看几遍,不再需要看书和资料了,很大程度上增强了我的信心,因为没有记在本子上的对我来说都是“常识”了,所有平时的要点都囊括在了笔记本里面了。如果你身边有面临高考的考生的话,请推荐给他们,这个方法会在高考中给他们很大的帮助。

3、学日语的经历。说来比较幸运,有机会能在大学里系统的学习一门二外。能在大学里学习一门外语确实是很有意义的一件事情,因为我们学习英语的战线已经有十几年了,但成果不是太显著,而二外作为一门你有选择的有主动性的去学习的一门语言,如果你肯花心思,进步是很快的。我是从大三开始系统学习日语的,那时刚考完六级,算是完成了阶段性的目标,于是转向日语的学习,洽好赶上学校与某一日企有合作的项目,很幸运的和同专业的几十名同学一起开始学日语,当时是一个中教讲语法,一个日本外教讲口语,因为学时较紧,所以基本上日语课占用了所有专业课之外的所有课时,一周要上20几个课时的日语。这个过程持续了一年多。在这一过程中对我来说受益最大的仍然是大量的笔记。“草稿式笔记”又一次发挥了它的神奇效果,我记了两个厚本子,记录了我整个学习语言的过程,从浅到深,翻翻笔记就能清晰地回忆起自己是怎么一路走过来的。当我有一段时间没有接触日语的时候,如果我想找回一些日语的感觉,我第一选择就是从头到尾的把笔记本看一遍,一遍看完后我仿佛就回到了以前学习日语时的状态,就像计算机一键还原一样,只有笔记能起到这个作用。

P.S顺便说明一下我为什么学日语的时候就很少去碰英语的东西。语言这东西学的多了真的是容易混,能精通好几国语言的人真的是值得敬佩,这种敬佩不仅仅是语言学习本身。学了两年的日语以后,口语之类的都练的比较熟悉,因为有外教环境,所以大家平时聊天沟通都得到了很好的锻炼,但是有点作英语面试的时候,我发现了一个很严重的问题,我竟然不能说出一整句表达清楚的英文介绍,每句话都不自觉的往外蹦日语,呵呵,英语的思维完全被日语给抑制住了,于是我发现,这两种语言冲突了,不过为了更好的学日语我就把英语放下了,直接最近的工作需要才再次捡起来,不过还好,把日语先放一放,英语自然就不受影响了。

4、目前的工作。几个月前换工作到了现在这家外企,外企的宽松的环境和人性化的制度让我很快喜欢上了这里,于是我抱定决心要在这里稳定下来,干出一番事业。从第一天入职我就领到了一个印有公司LOGO的笔记本,现在三个月时间吧,这个本子已经基本用完了。在这个本子上不仅有工作上的事情,meeting,需求讨论,还有大量的读书笔记,内容十分丰富,但都充实的记录了我的每天的轨迹。这本笔记同样比较“草稿”,但我能真正理解它,它就像我的一部分大脑一样,我现在走到哪里都会带着这个本子,班车上、轻轨上有大量的时间,拿出本子就好像和自己在对话一样,无比的踏实与充实。

三、读书要分级

古人早就说过“开卷有益”,在信息大爆炸的现代社会里,获取知识和信息的方式变的越来越多元化,网络的兴起让知识变的廉价,理想的说一根网线可以得到这个地球上你想知道的一切。请大家和我一起思考一个问题,不论是以什么样的形式,游戏也好,电影也好,看新闻,刷微博也好,我们的最终目的到底是想干什么。我的观点是,所有的这些行为总结起来,终极目的只有一个——获取信息。

回想在还没有Internet的时代里,人们主要获取信息的途径就是读书,这是那时的人们能想到的最直接的选择。而今天的我们选择就多了起来,读书看起来好像是最慢的一种获取知识的方式了。但是,选择多了就一定是好事吗?科学家证实(我也不知道哪个科学家),选择越多效率越差,因为这增加了你决策的时间和后悔的理由。先说上网吧,很简单的道理,大家都会经常体验到的,如果你想在网上集中作一件事情,往往是很难的,你会受到来自各个方面的干扰,各种“信息噪声”向你扑面而来,你常常会被另外的“新鲜”信息中断思路,比如QQ右下角讨厌的重大新闻浮动窗。再比如,如果想在电脑上踏下心来看一本PDF版的技术书也是很难的,开始可能还好,看上十页二十页以后,眼酸,想睡,看过什么没太深的印象,想干别的,效率很差,我就是这种情况,对需要用电脑阅读的大段的内容都有抵触的情绪。所以我避免用电脑看一些需要系统理解的东西,因为人有一种心理,就是对于我感到过抵触或厌倦的东西都不想再碰,所以我尽量不用这种方式去触及那些本身应该对我有很大意义的内容,我怕因为一时的不耐烦导致与这些经典失之交臂。我的习惯作法还是去阅读实体的书,这给人的感觉是完全不一样的,书的厚度是你可以真切感觉到的,而且,看实体书更容易深入内容,这可能因为人类一直以来的习惯都是以书为载体来学习,这促成了人脑的进化,人脑也更习惯这种传统的方式。

这部分的标题叫“读书要分级”,什么叫分级呢,就是读书的深度。

走进任何一家图书馆,你可能都有冲动要把这里的书全部读掉,可是这显然不现实。

电视剧《士兵突击》里许三多在团部的知识问答竞赛时,团长问他是怎么知道这么多知识的,许三多说“我这人笨,不知道应该看什么,我就跑到图书馆,从A开始看,现在才看到D”(大概是这意思,是不是D记不清了)。你说这种人真有吗?有,肯定有,但是这只能是个童话,对于一个现代化的大型图书馆来说,你就是光把A看完这辈子恐怕也不够用的。所以,面对浩瀚的书海,有选择的读一些经典之作才是明智之举。并不是每一本书对于我们的成长都是至关重要的,这里有经典,也充斥着垃圾。如果你花了两年读了一堆垃圾书,这样非但无益,反而会害了你。就像老罗曾经说的,他年轻的时候金庸的武侠小说非常火,很多的个体书商不知名的武侠作者出版的武侠书都署金庸的名,因为那时没有目录嘛,什么“飞雪连天射白鹿,笑书神侠倚碧鸳”,当时不知道,所以怎么把金庸的书挑出来,很难。

那怎么把经典的挑出来呢?这就要借助网络了,豆瓣读书是个不错的地方,这里都是一群爱读书的人,你要看的书在这里基本都能找得到,看过的读者会留下一些感想,如果评价太烂的话,你就应该果断放弃读这本书的想法,因为这可能会浪费你的时间,并且害了你。扯远了,下面开始分级。

第一级:经典中的经典——能奉为经典的著作不多,对于我们计算机这个领域,耳熟能详的经典也就是那一百多本吧,对于读这些书的态度就应该给予足够的重视,记笔记是必须的,因为这些思想需要与你的思想融合起来,形成你的理解,从而发挥经典的作用,因为大部分经典之所以长盛不衰就是因为它们都是授人以渔的。

第二级:技术类专著——这部分就是指那些资深技术专家,或在某个技术领域比较有建树的行业专家写的书,你选择这类书的目的性更强一些,往往是为了更深入的学习一门技术或熟悉一个行业而选择的,这部分你当然也要细读,因为技术的难度你更应该用笔记来梳理思路,形成整个技术的框架,这里面应该包括更多的细节,以助于以后翻阅的时候能理解的更深入。当然,你可以分章节来笔记,这样可以将一个大目标分割成小目标来激励自己去逐项完成。

第三级:技术基础类——有一些书目涉及的是你已经熟悉的技术领域但比较基础的内容,这些书的内容对你的技术水平来说比较基础,但你也不要忽视这样的基础书,这里面可能还有你没有吸收过的精髓也说不定。“温故而知新”,永远不要轻视基础,同样拿出你的本子,翻开新页,标上今天的日期,把这书中讲到的你意识模糊的内容记到你的本子里吧,可能你会发现,你要记的东西还真是不少呢。

第四级:技术周边类——比如《程序员》杂志、技术博客,这一类的资料也不要看过就扔,不要相信你是过目不忘的大师,你大脑的缓存是有限的,你必须要保存到硬盘!所以拿出你的本!

第五级:娱乐类——这部分与你的工作没有直接关系,属于你生活的部分,这部分的智慧更多,但是可能会和技术有冲突,所以如果你需要记笔记,请不要记到你记技术和工作的本子上。

四、只读经典

代码真得能改变世界,当然代码也正在改变着世界。程序员的时间是很宝贵的,我们不是这个星球上最聪明的一类人,但我们绝对是最有智慧的一类人。呱唧呱唧~

我们花了大量的时间去作需求,去Design,去Coding,去Testing,留给我们自己的时间却总是那么少,除去日常生活起居的时间,陪女朋友的时间,上下班的时间,留下读书的时间就没什么了,哦,对,忘了把你打Dota的时间算里了,这还哪有时间看书啊!!??

当程序员的没有不勤奋的,不勤奋的干不了程序员。所以书还是要看的,怎么看,只读经典。

关于什么是经典你自己决定咯,在这个圈里吃饭的,对技术都有很强的敏感性,坏书进不了你的眼,好书也一定逃不掉。

五、别吝惜你动笔的那点时间

说了这么多了,你可能已经意识到作笔记的重要性了,其实事实真的是这样。

你是否听说过某某大师一生作了几百本的读书笔记这些事,我以前一直有疑惑,他作笔记都是在干嘛呀?都记什么呀,有什么好记的?后来我真正开始作笔记以后,我才真正理解了这件事,原来大师之所以能成为大师,真的都不是一朝一夕的工夫,这需要持之以恒的坚持和坚定,这内心得多强大呀。你要知道,那几百本的笔记不只是笔记本那么简单,那些都是思想的沉淀,思想本来是抽象飘渺的,笔记这种形式让思想有了重量和形态,这种思想被组织,重构,索引,最终印到大脑里形成一座庞大的知识仓库。

很惭愧,我们国人没有记笔记的这个好习惯,而与我们相对的日本的整体国民,基本上都有记笔记的这个习惯,这是很可怕的。我的日语老师宫竹老师,她随身都会带有一个笔记本,随时记录所有她认为应该去记的东西,在很多方面我真的非常敬佩她,比如,我们当时有三个专业的大约二百名学生都上她的课,她能记住所有这些人的姓名,包括中文的和翻译成日文假名的,还不仅如此,其他不学日语的同学,只要与她作过自我介绍的甚至只有一面之缘的,她下次见面一定能叫出那人的名字,从没出过错。你以为这是她的记性好吗?绝对不是,这源于她记录的习惯,她不当面去记这些人的名字,但她私下肯定去写进本子里。光写进去就万事大吉了吗,不回头翻看怎么能记得住?这就是笔记的力量。关于日本人的很多好习惯,希望我以后有机会能发篇博客整体谈一下。

正所谓磨刀不误砍柴功,记笔记可能会影响你读一本书的速度,但它不会让你白读,如果你读书的目的不是想在你脑子里留下点什么的话还不如去看电视剧,读书就是为了长知识长学问长见识,就是为了将别人好的思想吸取过来,转化成自己的思想体系的一部分,当然,你应该有批判接受这些信息的能力,人云亦云的完全接受也是不合时宜的。

我有一个心愿,我渴望能有一天在公交、地铁里看到大家翻看的全是自己的笔记本,而不是一人拿着一部手机在那看着微博还傻笑!

原文链接:

]]>
Wed, 29 Aug 2012 09:08:51 +0000 viLuo http://viluo.com/post/14
IT自由职业者的成功秘诀 http://viluo.com/post/13 viLuo:不知道中国有多少自由职业的程序员,估计不会太多~~

我从事自由职业已有十余年了,有时候在我有全职工作时我把它当做副业,有时候我只做自由职业。关于自由职业中如何成功地编程,我也学到了一些经验。

1.做你会做的事情

客户付钱请你解决问题。他们对Haskell语言有多炫根本不感兴趣,而且他们也不想付钱让你去学习新的工具和编程语言。因为上一个程序员力不能及而退出或者被解雇,我接手这一类的项目,多的数不胜数。你应当拓展自己的技能以保持竞争力,而且不要因为其自身价值而过分喜爱时下流行的程序语 言。(注:Haskell是一种纯函数式编程语言。)

2.不要独树一帜

如果你掌握了一种新奇的、炙手可热的技术,那你当然可以使用它。但是,如果仅仅是因为看到会Lisp的自由职业者赚钱多就去学习Lisp,那你得仔细考虑一下自己如何跟他们竞争。当然,现今有很多PHP程序员,但大部分都是业余爱好者,你能轻易地跟他们竞争,并且有的是PHP工作去做。跟技术娴熟的、 有经验的专家竞争少数工作更困难。别人能赚钱的工作你都想去做,但不打算做的更好,所以你在与一支六年级的队伍打篮球,而不是试图去加入NBA。

3.不要试图做所有事情

注意不要忙于做技术支持、硬件安装、设计、艺术创作等工作。要坚持做自己擅长的。把那些你不擅长的工作发送给其它擅长的自由职业者,遇到同样的情况,他们也会发送给你。

4.不要接规模太大的工作

如果很难用一张纸把项目描述清楚,或者整个开发进度要持续几个月甚至几年,那么你得注意了。通常,规模较大的项目对一个人来说工作量太大。如果你非常想接一个大规模的项目,那么,试着把它分成若干个小的子项目,每个子项目可以短期完成并有一个可交付的成果,这样你和客户就可以看到项目的进度和成果。

5.避开行为恶劣的客户

如果客户看起来狂暴、专横、吝啬、贪婪、粗鲁、苛刻、霸道等,那么远离他们。去找其他客户。如果你很难想象与客户能够保持一种专业的、有报酬的工作关系,或者你感觉客户做的任何事情都很恶劣,那么拒绝他们。

6.大多数业务都是通过推荐得到的

好好干,让你的客户满意,与其他自由职业者合作,构建能够推荐你并给你带来工作的人际关系网。

7.不要试图让它看起来像个“真正的”交易

很多企业家过分注重积累公司的外在形象。你不需要花哨的网站、信头、办公室等。你可能甚至都不需要创建一个企业或者有限责任公司。除非你有员工或者一天能给客户开出72小时的账单,否则你不会赚到足够的钱来证明一切都制作精良或者能被认定为“专业”。你的客户不会在乎你在家工作,也不会在乎你没有排 场。

8.采用源码控制和问题跟踪

学会如何使用源码控制和基于网络的问题(bug)跟踪。在Wiki上记录每一个项目的需求、问题及决策。运用工具,让客户通过系统向你汇报bug。我使用ProjectLocker,现如今还有其他便宜的托管方案。不要浪费时间在你的服务器上搭建它。

9.追踪工作时间并定期开账单

获取报酬最好的方式就是准确地记录工作时间,并定期向客户开账单。我喜欢按小时记录工作,每两周向客户寄发票。你不需要一个成熟的记账系统,现在有很多时间跟踪托管软件以及像BlinkSale这样的在线开发票方案,它们能帮你完成大部分工作。

10.不要转包

我提倡与其他自由职业者合作,但我不喜欢将工作转包出去。很可能给别人讲解项目并审核其工作完成情况所花费的时间,与你亲自完成这项工作所花费的时间一样长。不管怎样,任何跟你水平相当的程序员花费的时间都是差不多的。如果你认为在Craigslist找到了一个每小时收费12美元的程序员是捡到了大 便宜,那祝你好运。如果你承担的工作多到你处理不了,那么试着将它转交给另一个懂得回报的自由职业者。

11.使用敏捷开发技术

客户都想了解工作进度。对需求的错误理解,可能是你犯的错误中代价最高的。采用短期可交付成果和反复审核的敏捷开发技术,会让你的客户满意,也会让你远离繁杂的任务。你不需要向用户讲解敏捷开发,他们根本不在乎。只是告诉他们你如何做以及你需要从他们那里得到什么。

12.让客户及时了解项目情况

在线发布工作进度,让客户了解你在做什么。对大部分人来说,编程很神秘,所以让你的工作进度清晰可见会使客户感觉跟你合作很舒服。我发现敏捷透明的开发过程可以防止账单纠纷,而且一旦客户习惯了你的高标准服务,他们便很少会把工作再交给其他自由职业者来做。

作者简介:作者GregJorgensen是一位典型的程序员,他从1974年开始编程。曾在耐克和苹果等公司任职。他专攻修复和完善受损、被遗弃和半生不熟的Web应用程序,尤其是后台语言是PHP的网站。

英文原文:

]]>
Tue, 28 Aug 2012 04:28:41 +0000 viLuo http://viluo.com/post/13
Web开发者应该注意的一些事项 http://viluo.com/post/12 能否开发出好的产品对Web开发者来说十分重要,开发者应该尽力让开发产品更加完美。然而在开发过程中,所有开发者都会犯一些同样的错误,这些错误会拖慢整个项目的进度,影响其他进程,并且耗费更多的时间和资金。

如果开发者能避免这些普遍的错误,会给公司、客户和自己节省许多时间和金钱。文章中介绍了一些注意事项,可以帮助web开发者们更好的完成开发工作,同时增加开发者在开发领域中的名气。

保持沟通

大部分开发者们都面临一个问题:在规划产品设计和需求时不善于和客户或公司进行沟通。客户和公司往往不清楚具体的开发工作。开发者应该详细的了解开发需求,向客户说明哪些功能可以实现,并且与客户商讨还要哪些功能。这样,开发工作会进行的更顺利。

了解项目规模

在开发时,开发者应该做好充分的调查工作,了解完成整个项目有哪些需要。同时开发者还要向客户和公司说清楚整个开发的所需开支、进度表和一些其他的必需条件。这样客户和公司可以为项目开发提供更好的条件,减少外界条件对开发工作的不利影响。

开发是一场马拉松

客户和公司大多都希望项目又快又好完成,但是这需要所有工作都衔接完美。开发者要掌握好开发中每个环节的节奏,不要为了客户或公司的进度而草草的完成开发中的任何一步,你需要确保项目中每个步骤都正常运行。真正想要好项目的公司和客户不会强加给开发者不实际的要求。

清楚自己的任务

一线开发者要清楚自己的任务,这一点非常重要。他们既不是项目经理,也不负责其他职位,他们最主要的工作就是开发,要学会处理好那些影响主要工作进程的职责,这些额外的职责会浪费你的时间和精力。

测试

开发者常犯的一个严重错误就是对项目的测试不够,并且总是独自测试。应该在不同的平台中对项目进行充分的测试——包括各种浏览器、PC、Mac、手机。还可以多方面招募其他人帮助测试,这样做不仅可以加快项目的步调,还可以全面检查程序中可能有的失误。

如果开发者在开发时能注意这些事项,就会减少许多阻碍和错误。

英文原文:

]]>
Tue, 28 Aug 2012 04:13:29 +0000 viLuo http://viluo.com/post/12
如何减小与“大牛”的差距 http://viluo.com/post/11 viLuo:这是我以前在iteye上看到的一片文章,作者是郗晓勇。当时看完,就把QQ状态改为了“要想人前显贵,必定人后受罪”。许多大牛不是天生的,更多的是靠时间的积累。在相同的时间里,如何让自己积累的比别人更多的,积累的知识更加有用,都是我们可以去思考的地方。

为什么同样的时间有的人可以漂亮的完成工作,而有些人废了很大的力气也没有完成?

前者我们常常称之为“大牛”,后者我们常常叫他们“菜鸟”。当然“大牛”都是相对而言的,“大牛”也不可能方方面面都非常厉害,换句话说大牛也不一定会沙王摇大,也不一定会船长放水。所谓的“大牛”他们只是在某些方面比我们强而已。但是值得注意的是,学习能力很强,效率普遍很高是一般“大牛”的共同特点。

是哪些因素造成了我们与“大牛”的差距呢?

背后的故事我们不一定知道

有这么两句话想必大家都应该听过“要想人前显贵,必定人后受罪”,还有一句就是“台上一分钟,台下十年功”。在这里想借助这两句话说得就是,不要只看到“大牛”如何如何厉害,人家以前是如何用功的你可没有看到呀!一味的和“大牛”比,感觉技不如人于是就自暴自弃,这是最幼稚的做法。

我们都不是刚出生的婴儿,起点必定是不一样的,就像上大学之后第一个期末考试一样,看着同宿舍几个哥们这个学期都没学习为啥人家考得好,偏偏就自己挂科了呢?笨蛋!因为人家高中的基础好啊!到了工作岗位上也是一样的道理,他是新人,我也是新人,为啥他的学习能力这么好呢,然后就开始怀疑自己的学习能力,怀疑自己是不是不适合干这行……一句话:因为人家之前接触过,所以学习起来驾轻就熟呀!

想起来《一万小时天才理论》里面的一句话:经过一万个小时的精深练习,一般的人都可以成为某一方面的专家。所以说人与人的能力实际上差不了多少,最终的决定性因素就是积累的时间长短而已。

去不去总结,会不会总结是同起点决胜的关键性因素

上面说了人与人的能力差不了多少,那么为什么同样没有基础的两个人学习新的知识会有学的好与学的不好之分呢?这里我想说的是—-总结!

很长一段时间一直在思考“总结”是什么?

宏观上说,总结就是把整个知识的体系变成自己的。举个例子:别人的代码很难看懂,但是自己的代码就很容易看懂。原因很简单,自己写出来的东西自己当然熟悉,实现过程以及函数的调用自己再熟悉不过了。但是如果是别人写的就要好好费费心思看上一看了。学知识也是如此,把别人的知识架构弄清楚,或者把别人的知识放到自己的知识架构中,于是自己对这方面的知识就会很熟悉,这就是我们所说的学知识“学会了”。

微观上说,总结就是我们通常说的“说白了……”以及“换句话说……”后面省略号所指的内容。相信大家都有这么一个感触:当你学高数或者数学分析的时候,如果去扣定理那么必定头大。但是如果是别人把定理用大白话给你讲一遍你便会觉得豁然开朗。微观上(比如某个定理,某个技术)的总结就是把那些看起来晦涩难懂的描述语言,转变成自己的话,让自己更好的理解与运用。

总结,一言以蔽之:把别人的知识另存为自己的就叫总结

对于时间管理是否给予足够的重视

上面的两点是“大牛”之所以成为“大牛”的重要原因,那么下一个问题就是我们如何减小与“大牛”的差距,更自信的说就是我们如何成为大牛!?

首先背后的努力这方面我们已经无能为力了,因为过去的时间已经成为过去,在别人发奋努力的时候我们已经让时间白白的流走了。其次总结的功夫也不是一朝一夕能够形成的。那么我们能做的就是从现在开始抓紧每分每秒开始我们自己“背后的努力”,养成总结的好习惯。这些都需要时间的打磨,光阴的锤炼,也就是说如果你现在不是大牛,那么要想以后成为“大牛”《时间管理》就是第一门课程。

关于时间管理想说的有以下两点

大块儿的时间不必多说需要认认真真的管理。经济学上有投入与收益的定义,个人认为学习上同样也存在投入与收益之说。学习一门知识,投入了金钱、投入了时间(这是关键,毕竟千金散尽还复来嘛)要的就是达到某种效果,即我们所说的收益。但是我们往往都是投入了没有收益,或者收益很少。究其原因无非是三心二意,效率低下等等。干着这件事脑子里想着另外一件事,更有甚者就和狗熊掰棒子一样,每件事都干一点到头来哪件都没有干完。这样的时间管理怎能有收益?

建议:管理自己的大块儿时间,一次只干一件事,这件事一定是相对而言最重要的,最紧急的。

零碎的边角时间也应该予以重视。看过刘未鹏《暗时间》的人都应该清楚,如果两个人总时间相等,那么区分两人水平的就是对“暗时间”的利用。(具体参考刘未鹏的《暗时间》一书)。这就是告诉我们,重视别人重视的最多你会和别人一样,重视无论别人重不重视的你将超越别人。零碎的时间一样可以用来思考,用来学习,看你去如何对待他们了。

建议:罗列一下自己有哪些零碎时间,能否用学习(例如背单词,回忆昨天的学习内容等)填充他们,而不是让这些零碎时间浪费在发呆上。

怎样时间管理

这里给大家推荐一个时间管理的工具,谷歌日历。其实大家都知道,希望今天忙碌的您能特地拿出几个小时好好学习一下谷歌日历的帮助,然后尝试着安排自己的时间,成为“大牛”从今天开始!

PS:

很多的时候给自己制定计划一般都是完不成,这样的话一次两次坚持制定,后面三次四次就不想再制定计划了,感觉这是在浪费时间。

其实这要归咎于不能客观的了解自己,高估或者低估了自己的能力。了解自己和了解别人是一样的,是需要时间的,怎么可能一次就准确透彻呢?

每次给自己制定计划看看完成的情况,然后下次再制定的时候逐渐调整计划所需的时间长短。时间管理不是意味着一定要在某个时间段之内完成什么,而是努力使自己的时间达到最大化的利用。设想一下如果没有计划那么工作或者学习的效率会更低,从这方面来看你就会发现只要是去管理时间,只要是去定计划都是一种进步的表现。

]]>
Mon, 27 Aug 2012 08:19:09 +0000 viLuo http://viluo.com/post/11