React笔记

参考:http://www.ruanyifeng.com/blog/2015/03/react.html

其中,React在ES6中去掉了getInitialState这个hook函数,规定state在constructor中实现。

自己用Demo:

var React = require('common:static/libs/react/react.min.js');
var ReactDOM = require('common:static/libs/react/reactdom.min.js');
var data =1;

var names=[
  <h1>Hello,World</h1>,
  <h2>React</h2>
];

var HelloMessageAnother = React.createClass({
  render:function(){
    return <div>Hello,{this.props.name}!</div>;
  }
});

var HelloMessage = React.createClass({
  render(){
    return <div>Hello,{this.props.name}!</div>;
  }
});

var MyTitle = React.createClass({
  propTypes:{
    title: React.PropTypes.string.isRequired,
  },
  
  render:function(){
    return <h1>{this.props.title}</h1>
  }
});

var MyTitleAnother = React.createClass({
  getDefaultProps:function(){
    return{
      title:'Hello World'
    };
  },
  render:function(){
    return <h1> {this.props.title} </h1>;
  }
});

var TestDom = React.createClass({

  handleClick:function(){
    this.refs.myTextInput.focus();
  },
  render:function(){
    return(
      <div>
        <input type="text"  ref="myTextInput" />
        <input type ="button" value = "focus the text input" onClick={this.handleClick}/>
      </div>
      );
  }

});

var LikeButton = React.createClass({
   getInitialState:function(){
    return {liked:false};
   },

   changeLike:function(){
    this.setState({liked:!this.state.liked});
   },

   render:function(){
    var text = this.state.liked?'like':'haven\'t liked';
    return(
      <p onClick={this.changeLike}>
        You {text} this. Click to toggle.
      </p>
      );
   }
});

var InputChangeState = React.createClass({
  getInitialState:function(){
    return {text:"Hello"};
  },

  changeInput:function(event){
    this.setState({
      text:event.target.value
    });
  },

  render:function(){
    var value = this.state.text;
    return (
      <div>
        <input type="text" onChange = {this.changeInput} />
        <p>{value}</p>
      </div> 
    );
  }
});

var OpacityComponent = React.createClass({
  getInitialState:function(){
    return {opacity:1.0};
  },

  componentDidMount:function(){
    this.timer = setInterval(function(){
      var opacity = this.state.opacity;
      opacity -= .05;
      if(opacity<0.1){
        opacity = 1.0;
      }
      this.setState({
        opacity:opacity
      });
    }.bind(this),100);
  },

  render:function(){
    return (
      <div style={{opacity:this.state.opacity}}>
        Hello,{this.props.name}
      </div>
      );
  }
});


ReactDOM.render(
  // <HelloMessage name="john"/>,
    // document.getElementById('test')

  // <MyTitle title={data} />,
  // document.body

  // <MyTitleAnother/>,
  // document.body

  // <TestDom />,
  // document.getElementById("test")

  // <LikeButton />,
  // document.getElementById("test")

  // <InputChangeState />,
  // document.getElementById("test")

  <OpacityComponent name="World"/>,
  document.getElementById("test")
);

ES6:

var React = require('common:static/libs/react/react.min.js');
var ReactDOM = require('common:static/libs/react/reactdom.min.js');

class SubmitOrderComponent extends React.Component{
	constructor(props){
		super(props);
		this.state = {
			ownerTel : "aaa"
		}
	}


	render(){
	    let self=this;	
	    return(
	        <div>
		//代码 注意外层必须有其他标签包着,不然会报错
		</div>
	    );
	}
};

ReactDOM.render(
	<SubmitOrderComponent />,
	document.getElementById("submitOrder")
);

基本流程:

var React = require('common:static/libs/react/react.min.js');
var ReactDOM = require('common:static/libs/react/reactdom.min.js');

class ArrayComponent extends React.Component{
constructor(props){
    super(props);
    this.state = {
      ownerTel : "aaa"
    }
  }

  getTest(){
    return (
      <h1>Hello</h1>
    );
  }

  render(){
    let self = this;
    return( 
      <div> //没有这个标签将会报错
        <div>Hello,ES6 {self.state.ownerTel}</div>
        {self.getTest()}
      </div>
    );
  }
};

ReactDOM.render(
  <ArrayComponent />,
  document.getElementById("test")
);

在所有的return中如果是一个标签可以直接返回,如果多个标签必须最外面再包一层,要保证return回的只有一个元素。

关于数组遍历:

var nameArray=[
  {
    "name":"aaaa",
    "age":"12"
  },
  {
    "name":"bbb",
    "age":"13"
  }
]
  //如下两种方法都可以进行遍历
 getArrayElement(){
    var str = "";
    for (var i = 0; i < nameArray.length; i++) {
      str = str + " " + nameArray[i].name + "/" + nameArray[i].age ;
    }
    return str;
  }

  getArrayMap(){
    var str = "";
    nameArray.map(function(n){
      str = str + " " + n.name + "/" + n.age;
    })
    return str;
  }

关于map:

var nameArray=[
  {
    "name":"aaaa",
    "age":"12"
  },
  {
    "name":"bbb",
    "age":"13"
  }
]

  getArrayMap(){
    return(
      nameArray.map(function(n){
        return <div>" " + n.name + "/" + n.age;</div>
      })
    )
  }

//对于map,因为其里面的函数function(n),会遍历数组生成
对应<div></div>包含的语句,在数组遍历外层需要用return()再
将返回的每条语句返回,这样就会打印出多条<div></div>包含的语句了。
但是此时<div></div>中的变量并未识别,还需要用{}.
所以正确的应该是:


  getArrayMap(){
    return(
      nameArray.map(function(n){
        return <div>{ " " + n.name + "/" + n.age}</div>
      })
    )
  }

 

关于函数调用:

非render中函数调用直接用:this.函数名(),如果在render中用self.函数名.bind(this)

支付宝手机网站支付集成笔记

一、创建应用:

https://openhome.alipay.com/platform/developerIndex.htm开发者中心创建应用,然后签约手机网站支付,提交审核,一般需要一天时间审核。

二、配置应用信息:

如下图:

因为第三步已经上传了公钥,所以显示如上图。未设置情况显示“设置应用公钥”。

1.设置应用公钥步骤:

https://doc.open.alipay.com/docs/doc.htm?treeId=291&articleId=105971&docType=1页面根据自己系统下载windows版或者mac版的生成密钥工具,然后按照文档操作即可。

我选择的是PKCS8(JAVA适用),密钥长度选择2048,点击生成密钥后,将商户应用私钥复制到代码中使用,将公钥上传(也就是“设置应用公钥”点击后的文本框,在上图3的位置)。上传成功后如3所示,点击查看支付宝公钥,将公钥粘贴到代码中使用。

三、集成:

官方集成文档地址:https://doc.open.alipay.com/doc2/detail?treeId=203&articleId=105288&docType=1

demo:

if(request.getParameter("WIDout_trade_no")!=null){
    // 商户订单号,商户网站订单系统中唯一订单号,必填
    String out_trade_no = "自己设置的订单号";
    // 订单名称,必填
    String subject = "赛事赞助";
    // 付款金额,必填 填每笔订单的金额
    String total_amount=100+"";
    // 商品描述,可空
    String body = "钛度杯war3大师赛众筹奖金";
    // 超时时间 可空 支付的超时时间,单位是分
   String timeout_express="15m";
    // 销售产品码 必填
    String product_code="QUICK_WAP_PAY";
    /**********************/
    // SDK 公共请求类,包含公共请求参数,以及封装了签名与验签,开发者无需关注签名与验签     
    //调用RSA签名方式
    AlipayClient client = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID, AlipayConfig.RSA_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET, AlipayConfig.ALIPAY_PUBLIC_KEY,AlipayConfig.SIGNTYPE);
    AlipayTradeWapPayRequest alipay_request=new AlipayTradeWapPayRequest();
    
    // 封装请求支付信息
    AlipayTradeWapPayModel model=new AlipayTradeWapPayModel();
    model.setOutTradeNo(out_trade_no);
    model.setSubject(subject);
    model.setTotalAmount(total_amount);
    model.setBody(body);
    model.setTimeoutExpress(timeout_express);
    model.setProductCode(product_code);
    alipay_request.setBizModel(model);
    // 设置异步通知地址
    alipay_request.setNotifyUrl(AlipayConfig.notify_url);
    // 设置同步地址
    alipay_request.setReturnUrl(AlipayConfig.return_url);   
    
    // form表单生产
    String form = "";
	try {
		// 调用SDK生成表单
		form = client.pageExecute(alipay_request).getBody();
		response.setContentType("text/html;charset=" + AlipayConfig.CHARSET); 
	    response.getWriter().write(form);//直接将完整的表单html输出到页面 
	    response.getWriter().flush(); 
	    response.getWriter().close();
	} catch (AlipayApiException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} 
}

 

关于自定义参数:

该字段可以加自定义参数,但是我用JSON格式字符串会报错,提示系统繁忙,请稍候再试,或者是支付超时,所以最后把参数用String组合了一下,注意必须用URLEncoder编码,代码如下:

String paramsTmp = uname + ":" + sid;
        String params = null;
        try {
            params = URLEncoder.encode(paramsTmp, "UTF-8");
        } catch (UnsupportedEncodingException ex) {
            Logger.getLogger(AlipayService.class.getName()).log(Level.SEVERE, null, ex);
        }
        model.setPassbackParams(params);

然后支付宝异步通知会原样带回自定义参数,取参数方法:

String unameStrTmp = request.getParameter("passback_params");
String unameStr = URLDecoder.decode(unameStrTmp, "UTF-8");

不同于取其他参数的方法:

String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");

取到参数后就可以根据自己定义的参数类型进行处理了。

四、关于沙箱环境:

1.沙箱环境需要重新在沙箱环境配置应用环境,也就是步骤二。沙箱环境和正式环境的配置有以下五处不同:

//沙箱环境appid,可以在沙箱应用配置找到
public static String APPID = "2016080100144644";
//沙箱环境固定地址
public static String URL = "https://openapi.alipaydev.com/gateway.do";
//沙箱环境下的支付宝公钥(因为用了和正式环境同一套密钥,所以只需要改支付宝公钥即可,如果不是同一套还需要改私钥)
public static String ALIPAY_PUBLIC_KEY = "";
//支付宝通知地址,必须外网可以访问,该地址在沙箱环境下可以收到支付宝通知
public static String notify_url_web = "http://101.201.76.66:8080/api/alipay/mobilenotify";
public static String return_url_web = "http://101.201.76.66:8080/app/fund/paycallback";

 

2.沙箱应用配置、沙箱账号、沙箱工具都可以在开发者中心的如下页面找到。

 

3.沙箱环境配置好后,需要下载沙箱工具进行测试,也就是一个测试环境下的支付宝app,就是在上图的沙箱工具中进行下载(只有安卓版,没有IOS版),账号在上图沙箱账号中获取,里面有很多钱可以进行测试。

 

附:支付宝官方沙箱环境使用说明:https://doc.open.alipay.com/doc2/detail.htm?treeId=200&articleId=105311&docType=1

五、测试通过后,记得改回正式环境,然后就可以上线了。

尤其注意集成时如果需要带自定义参数,可能会有问题,当时就是自定义参数坑了半天,同时还要考虑编码。

ps:因为是后端直接返回了form表单,所以前端必须在当前网页打开,不能另起网页,另起网页好像会有问题,就是点下订单了没反应。

Lua学习笔记

一、Lua安装:

1.  Lua官网下载安装,“Lua for Windows”下载地址如下:

http://luaforwindows.luaforge.net/  (需要翻墙)

ps:安装Lua时,需要预习安装VC2005运行时库,否则安装时会出错,Microsoft Visual C++ 2005 SP1下载地址:https://www.microsoft.com/en-us/download/confirmation.aspx?id=5638

2.安装之后就可以打开SciTE,输入lua代码:

print("hello,lua")

然后保存文件(注意后缀名必须为.lua),然后按F5运行程序,可以看到输出窗口如下显示:

则代表整个Lua开发环境安装成功。

ps:也可以直接打开Lua快捷方式,在里面编辑程序。

Redis学习笔记

简介

Remote Dictionary Server (Redis) ,Redis是一个开源的高性能键值对数据库。

ps:Redis官网没有windows版本,需要去github下载。

下载地址:https://github.com/dmajkic/redis/downloads。或者https://github.com/MicrosoftArchive/redis/releases

一、启动:

打开一个cmd窗口 使用cd命令切换目录到安装目录运行 redis-server.exe redis.windows.conf

这时候另启一个cmd窗口,原来的不要关闭,不然就无法访问服务端了。

切换到redis目录下运行 redis-cli.exe -h 127.0.0.1 -p 6379 ,或者直接运行redis-cli

Redis选择数据库命令:select 0 选择到0数据库( redis默认的数据库是0~15一共16个数据库)

注:

redis可视化工具下载地址:https://redisdesktop.com/download

二、Redis数据类型

Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

String(字符串):

string是redis最基本的类型,一个key对应一个value。

使用set命令设置key和value,使用get得到value。

set    key   value

get    key

注意:一个键最大能存储512MB。

Hash(哈希):

Redis hash 是一个键值对集合。

Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。

以上实例中 hash 数据类型存储了包含用户脚本信息的用户对象。 实例中我们使用了 Redis HMSET, HEGTALL 命令,user:11915为键值。

每个 hash 可以存储 232 – 1 键值对(40多亿)。

List(列表):

Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。

然后再添加一个元素到左边:

列表最多可存储 232 – 1 元素 (4294967295, 每个列表可存储40多亿)。

Set(集合):

Redis的Set是string类型的无序集合。

集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。

sadd 命令

添加一个string元素到,key对应的set集合中,成功返回1,如果元素以及在集合中返回0,key对应的set不存在返回错误。

命令:sadd key member

然后再添加一个相同value的元素:

注意:以上实例中 beijing添加了两次,但根据集合内元素的唯一性,第二次插入的元素将被忽略。

集合中最大的成员数为 232 – 1 (4294967295, 每个集合可存储40多亿个成员)。

zset(sorted set:有序集合):

Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。

不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。

zset的成员是唯一的,但分数(score)却可以重复。

zadd 命令

添加元素到集合,元素在集合中存在则更新对应score

命令:zadd key score member

三、Redis事务

一般情况下redis在接受到一个client发来的命令后会立即处理并 返回处理结果,但是当一个client在一个连接中发出multi命令有,这个连接会进入一个事务上下文,该连接后续的命令并不是立即执行,而是先放到一 个队列中。当从此连接受到exec命令后,redis会顺序的执行队列中的所有命令。并将所有命令的运行结果打包到一起返回给client.然后此连接就 结束事务上下文。

四、Redis脚本

eval命令:

EVAL script numkeys key [key ...] arg [arg ...]

以上为eval的语法格式,其中:

<1> script:     自己的lua脚本

<2> numkeys:  key的个数

<3> key:         redis中各种数据结构的替代符号

<4> arg:         你的自定义参数

注意: numkeys是key的个数,KEYS和ARGV必须要大写。

 

使用Redis和lua脚本文件:

lua脚本文件内容:

return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}

然后cmd,cd到Redis的安装目录,执行命令:

可以发现成功调用了lua脚本。

重要:其中的参数个数2可以省略,但是一定要有逗号,且逗号前后必须要有空格。否则,会造成lua脚本ARGV为nil的问题。

特别注意:不能使用Redis客户端执行以上操作,如果用Redis客户端的eval命令,则可能会报以下错误:

(error) ERR Error compiling script (new function): user_script:1: ‘<name>’ expected near ‘\’

或者:

(error) ERR value is not an integer or out of range

 

五、Redis远程连接:

cd到本地redis目录,然后执行:redis-cli -h  IP地址 -p 6379 -a 密码

Cloneable接口

基本类型:存放在栈空间中。
引用类型:存放在堆空间中。

 

如果类没有实现Cloneable接口,调用类对象的clone方法会抛出CloneNotSupportedException,Object提供的clone方法是浅度复制的。

Demo:

public class TestCloneable {

    public static void main(String[] args) throws CloneNotSupportedException {
        Name name = new Name("jack");
        Person person = new Person(name, 20);
        Person clonePerson = (Person) person.clone();
        name.setFirstName("john");
        person.setAge(18);
        System.out.println("name is:" + clonePerson.getName().getFirstName() + ",age is:" + clonePerson.getAge());
    }

}

class Name {

    private String firstName;

    public Name(String firstName) {
        this.firstName = firstName;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
}

class Person implements Cloneable {

    private Name name;
    private int age;

    public Person(Name name, int age) {
        this.name = name;
        this.age = age;
    }

    public Name getName() {
        return name;
    }

    public void setName(Name name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

输出结果:name is: john, age is: 20

因为clone后,clonePerson的name是引用类型,所以当原name的值变化后,clonePerson的值也会变,age是基本类型,clone后不变。(引用类似于指针,变化后指向堆中不同的地方。)

前端开发

 前端开发环境

环境搭建


知识储备

  • velocity——java后端模板
  • fis3——百度开发的,前端工程化工具
  • jello——基于Java的前端集成解决方案,就当他是fis3好了
  • jQuery——js基础api
  • bootstrap——主题+组件、帮助迅速开发样式
  • less——css预处理语言

前端目录介绍

目录/文件名 作用
page 所有页面入口模板
resource 运营资源目录,本地测试用
static 静态、公用资源目录
test 本地测试用,模拟后端数据(静态 + ajax接口)
widget 页面片断,用于组成页面的组件 + 模块
.gitignore git忽略文件配置,如果用的svn此文件没用
fis-conf.js fis3配置文件,js/css/图片优化打包、服务器部署等
README.md 说明文档,可能也没写什么有用的东西
server.conf 本地测试用,路由配置

一、fis3环境配置

参考:http://fis.baidu.com/fis3/docs/beginning/install.html

  1. 安装node(包含npm),版本号:4.x(5.x不兼容)
    http://nodejs.cn/
  2. 安装fis3
    1. npm install -g fis3

    如果国外源安装不上,试试淘宝的源

    1. npm install -g fis3 --disturl=http://registry.npm.taobao.org/mirrors/node --registry=http://registry.npm.taobao.org
  3. 执行 fis3 -v 看看是否安装成功,如果你看到一个老漂亮的FIS你就成功了
  4. 安装项目fis插件
    1. npm install -g fis3-jello@1.0.8
    2. npm install -g fis3-server-jello
    3. npm install -g fis-parser-less@0.1.3
    4. npm install -g fis3-hook-amd@0.1.14
    5. npm install -g fis3-parser-reactjs@1.0.0
    6. npm install -g fis3-preprocessor-extlang@1.1.7
    7. npm install -g fis-parser-babel-5.x
    • fis3-parser-less是网友贡献的插件,有bug,不要安装

二、本地调试

参考:https://github.com/fex-team/jello

  1. 命令行cd到前端开发目录web
  2. 启动fis3本地服务器,使用8081端口
    1. fis3 server start -p 8081 --type jello
  3. 执行发布命令
    1. fis3 release -w
  4. 打开http://127.0.0.1:8081

三、与后端联调开发(已经不用这种方式了)

  1. 配置前端开发目录web根目录下的fis-conf.js文件,找到以下一坨配置代码
    1. fis.media('rd').match('*', {
    2. deploy: fis.plugin('http-push', {
    3. receiver: 'http://127.0.0.1:8080/upload',
    4. // receiver: 'http://172.20.198.179:8080/upload',
    5. to: '/Users/erin/new-work/laosiji/chosengamer/backend/spring-mongo-demo/target/demo-1.0.0' // 注意这个是指的是测试机器的路径,而非本地机器
    6. })
    7. });

    receiver改成 http://<后端局域网ip>:8080/upload
    to改成后端 spring-mongo-demo/target/demo-1.0.0 目录,后端是window机器记得加盘符

  2. 在确保后端项目启动的前提下,在web目录下执行命令行
    1. fis3 release rd
  3. 访问http://<后端局域网ip>:8080/

其他——前端推荐使用sublime编辑器

  1. 安装sublime3
  2. 从菜单 ViewShow Console 或者 ctrl + ~ 快捷键,调出console。将以下 Python 代码粘贴进去并 enter 执行
    1. import urllib.request,os; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); open(os.path.join(ipp, pf), 'wb').write(urllib.request.urlopen( 'http://sublime.wbond.net/' + pf.replace(' ','%20')).read())
  3. ctrl + shift + p 并在输入框输入 ip (install package的缩写), enter 安装
  4. 在输入框输入 velocityenter 安装默认插件,用于高亮.vm文件
  5. 重复步骤3,在输入框输入 lessenter 安装默认插件,用于高亮.less文件

前端页面开发流程


本文以一个todo-list页面为?:
页面url为/demo/todo-list,实现对list增删改操作,布局使用老司机的通用页头页尾。

1. 创建本地路由

修改根目录下的server.conf文件,增加路由条目

  1. rewrite ^\/demo\/todo-list$ /page/demo/todo-list

2. 创建页面vm

根据路由配置,在/page/demo目录下创建todo-list.vm

Java反射

Demo:

package testreflect;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Vector;

public class Car {

    private String name;
    private String color;
    private int price;
    
    public Car() {

    }

    public Car(String name, String color, int price) {
        this.name = name;
        this.color = color;
        this.price = price;
    }

    public void printDetails() {
        System.out.println(MessageFormat.format("name={0},color = {1},price = {2}", new Object[]{name, color, price}));
    }

    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

}

package testreflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;

public class TestReflect {
    
    public static void main(String[] args) {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        Class clazz = null;
        try {
            clazz = loader.loadClass("testreflect.Car");
            Constructor constructor = clazz.getDeclaredConstructor((Class[])null);
            
            Car car = (Car)constructor.newInstance();
            
            Method setName = clazz.getMethod("setName", String.class);
            setName.invoke(car, "audi");
            Method setColor = clazz.getMethod("setColor", String.class);
            setColor.invoke(car, "red");
            Method setPrice = clazz.getMethod("setPrice", int.class);
            setPrice.invoke(car, 1000);
            car.printDetails();
            
        } catch (ClassNotFoundException ex) {
            System.out.println("Exception caught");
        } catch (NoSuchMethodException ex) {
            System.out.println("Exception caught1");
        } catch (SecurityException ex) {
            System.out.println("Exception caught2");
        } catch (InstantiationException ex) {
            System.out.println("Exception caught3");
        } catch (IllegalAccessException ex) {
            System.out.println("Exception caught4");
        } catch (IllegalArgumentException ex) {
            System.out.println("Exception caught5");
        } catch (InvocationTargetException ex) {
            System.out.println("Exception caught6");
        }  
    }
    
}

输出结果:

name=audi,color = red,price = 1,000

 

类装载器ClassLoader

类装载器工作机制

类装载器就是寻找类的节码文件并构造出类在JVM内部表示对象的组件。在Java中,类装载器把一个类装入JVM中,要经过以下步骤:

      [1.]装载:查找和导入Class文件;
      [2.]链接:执行校验、准备和解析步骤,其中解析步骤是可以选择的;
          [2.1]校验:检查载入Class文件数据的正确性;
          [2.2]准备:给类的静态变量分配存储空间;
          [2.3]解析:将符号引用转成直接引用;
    [3.]初始化:对类的静态变量、静态代码块执行初始化工作。

 

ClassLoader重要方法

在Java中,ClassLoader是一个抽象类,位于java.lang包中。下面对该类的一些重要接口方法进行介绍:

    •   Class loadClass(String name)
    • name参数指定类装载器需要装载类的名字,必须使用全限定类名,如com.baobaotao. beans.Car。该方法有一个重载方法loadClass(String name ,boolean resolve),resolve参数告诉类装载器是否需要解析该类。在初始化类之前,应考虑进行类解析的工作,但并不是所有的类都需要解析,如果JVM只需要知道该类是否存在或找出该类的超类,那么就不需要进行解析。

    • Class defineClass(String name, byte[] b, int off, int len)
    • 将类文件的字节数组转换成JVM内部的java.lang.Class对象。字节数组可以从本地文件系统、远程网络获取。name为字节数组对应的全限定类名。

    •   Class findSystemClass(String name)
    • 从本地文件系统载入Class文件,如果本地文件系统不存在该Class文件,将抛出ClassNotFoundException异常。该方法是JVM默认使用的装载机制。

    •   Class findLoadedClass(String name)
    • 调用该方法来查看ClassLoader是否已装入某个类。如果已装入,那么返回java.lang.Class对象,否则返回null。如果强行装载已存在的类,将会抛出链接错误。

  •   ClassLoader getParent()
  • 获取类装载器的父装载器,除根装载器外,所有的类装载器都有且仅有一个父装载器,ExtClassLoader的父装载器是根装载器,因为根装载器非Java编写,所以无法获得,将返回null。

 

Java反射机制

Class反射对象描述类语义结构,可以从Class对象中获取构造函数、成员变量、方法类等类元素的反射对象,并以编程的方式通过这些反射对象对目标类对象进行操作。这些反射对象类在java.reflect包中定义,下面是最主要的三个反射类:

    • Constructor:类的构造函数反射类,通过Class#getConstructors()方法可以获得类的所有构造函数反射对象数组。在JDK5.0中,还可以通过getConstructor(Class… parameterTypes)获取拥有特定入参的构造函数反射对象。Constructor的一个主要方法是new Instance(Object[] initargs),通过该方法可以创建一个对象类的实例,相当于new关键字。在JDK5.0中该方法演化为更为灵活的形式:newInstance (Object… initargs)。
    •  Method:类方法的反射类,通过Class#getDeclaredMethods()方法可以获取类的所有方法反射类对象数组Method[]。在JDK5.0中可以通过getDeclaredMethod(String name, Class… parameterTypes)获取特定签名的方法,name为方法名;Class…为方法入参类型列表。Method最主要的方法是invoke(Object obj, Object[] args),obj表示操作的目标对象;args为方法入参。在JDK 5.0中,该方法的形式调整为invoke(Object obj, Object… args)。此外,Method还有很多用于获取类方法更多信息的方法:
    • 1)Class getReturnType():获取方法的返回值类型;

            2)Class[] getParameterTypes():获取方法的入参类型数组;
            3)Class[] getExceptionTypes():获取方法的异常类型数组;
          4)Annotation[][] getParameterAnnotations():获取方法的注解信息,JDK 5.0中的新方法;

  •  Field:类的成员变量的反射类,通过Class#getDeclaredFields()方法可以获取类的成员变量反射对象数组,通过Class#getDeclaredField(String name)则可获取某个特定名称的成员变量反射对象。Field类最主要的方法是set(Object obj, Object value),obj表示操作的目标对象,通过value为目标对象的成员变量设置值。如果成员变量为基础类型,用户可以使用Field类中提供的带类型名的值设置方法,如setBoolean(Object obj, boolean value)、setInt(Object obj, int value)等。

摘录:http://www.iteye.com/topic/1123081

Singleton单例模式

Singleton(单例模式):

单例模式用来保证整个程序中某个实例有且只有一个。

单例模式有两种构建方式:

  • 饿汉方式:用static修饰,在类装载时构建。不适用于初始化时构造器非常耗时的类。
  • 懒汉方式:指全局的单例实例只有在第一次被使用时才构建,延迟初始化。

1.饿汉式单例类

public class TestSingleton
 
    private static TestSingleton instance = new TestSingleton();
 
    //把默认的构造方法设置为私有的,这样外界就无法通过构造方法来创建实例
    private TestSingleton() {}
 
    //静态方法,用来返回类的唯一实例
public static TestSingleton getInstance(){
return instance;
}
}
  • 饿汉模式因为是用static修饰,在类装载时构建,所以会导致加载类时比较慢,如果构造器内的方法比较耗时,则加载过程会比较长。
  • 但饿汉模式是线程安全的,运行时获取对象速度比较快。

2.懒汉式单例类

public class TestSingleton {
    //把默认的构造方法设置为私有的,这样外界就无法通过构造方法来创建实例
    private TestSingleton() {
    }

    private static TestSingleton instance = null;

    //静态方法,用来返回类的唯一实例,因为用synchronized加锁力度太大,所以使用double check的方式。
    public static TestSingleton getInstance() {
        if (instance == null) {
            synchronized (TestSingleton.class) {
                if (instance == null) {

                    //instance在初始化过程中,可能已经不为null,所以加一临时变量t,确保初始化完成后再赋值给instance。
                     TestSingleton t = new TestSingleton();
                     instance = t;
                 }
            }
        }
        return instance;
        }
}

其中instance = new TestSingleton()这句,这并非是一个原子操作,事实上在 JVM 中这句话大概做了下面 3 件事情:

  1. 给 instance 分配内存
  2. 调用TestSingleton 的构造函数来初始化成员变量,形成实例
  3. 将instance对象指向分配的内存空间(执行完这步 instance才是非 null 了)

但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,就会报错,所以我们使用一个临时变量,确保初始化完成后再赋值给instance。

  • 懒汉模式的特点是加载类时比较快,但是在运行时获取对象的速度比较慢,线程不安全, 懒汉式如果在创建实例对象时不加上synchronized则会导致对象的访问不是线程安全的。

ArrayList和Vector ArrayList和LinkedList

ArrayList、Vector和LinkedList都实现了List类。

 

ArrayList和Vector:

  •  Vector的方法都是同步的(Synchronized),是线程安全的(thread-safe),而ArrayList的方法不是;
  • ArrayList性能比Vector好(因为Vector是线程安全的)
  • 两者都是采用的线性连续空间存储元素,但是当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%的大小。

ArrayList和LinkedList:

  • ArrayList的内部实现是基于内部数组Object[],所以从概念上讲,它更象数组,适合随机访问和遍历;
  • LinkedList是用链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较慢。