JavaScript所谓的”关联数组”

“关联数组”是一种具有特殊索引方式的数组。不仅可以通过整数来索引它,还可以使用字符串或者其他类型的值(除了NULL)来索引它。
————百度百科

大家肯定熟悉这样一种数据结构:Key-Value键值对的集合。在Java语言中用Map来实现,十分方便。
而在JavaScript中只有Array这种内置对象供开发者使用。

var a = new Array();
a[0]=0;
a[1]=1;
a.push(2);

alert(a[2]);//2

或者这样


var a = [0,1,2]; alert(a[2]);//2

等等,索引为数字。所以我在网上查找Array是否支持关联数组,也就是是否可以用数字以外的对象作为Key。网上有博客写可以支持关联数组。
比如

var a = new Array();
a["one"]=1;
a["two"]=2;

alert(a["two"])//2

也可以用对象

var b = new Object();
a[b] = 2;

alert(a[b]);//2

甚至给出了for each循环

var a = new Array();
var b = new Object();
a[b] = 2;
for(var key in a){
    alert(a[key]);//2
}

看到这如果你相信了”关联数组”,那恭喜你掉进了一个大坑。
首先JavaScript内置对象Array是不支持数字以外的对象作为key的。如果你不信

var a = new Array();
a[2]=2;//指定第三位,数组的长度就是3
alert(a.length);//3

var a = new Array();
a["two"] = 2;
alert(a.length);//0

var b = new Object();
a[b] = 2;
alert(a.length);//0

这说明什么问题,我们储存的数据根本不在数组的结构中。那我们既然之前能取到值,这些数据存放在哪里了呢。
其实这些值作为对象的属性保存起来了。我们甚至不需要Array这个内置对象,只需要随便一个对象就OK了

var a = new Object();
a["two"] = 2;

alert(a["two"]);//2

这个等价于

var a = {"two":2};

alert(a["two"]);//2

那么我们在使用这种方式实现”关联数组”的过程中会遇到哪些问题
首先不要当这是数组,所以内置对象Array所提供的方法不可用

a.length==0 //true

既然这是保存属性,数据结构应该是指针结构,那么Key应该是String类型的,我们来验证一下。

var a = new Array();
var b = new Object();

a[b] = 2;
for(var key in a){
    alert(typeof key);//string
}

可以看到,虽然我们储存时使用Object,但遍历时,Key就是string类型。
进一步猜想,在保存属性的时候,自动把作为Key的对象转换成string类型。我们继续验证,这里重写toString方法。

var a = new Array();
var b = new Object();
b.toString = function(){
    return "IamKey";
}
a[b] = 2;
for(var key in a){
    alert(key);//"IamKey"
}

这导致一个严重的后果就是我们使用自定义对象作为key时,因为没有重写toString方法,使得Key值相同,后来的数据覆盖前数据。

最后我们来反思一下为什么JavaScript会让人误以为有关联数组。罪魁祸首是访问属性的方式。
我们都知道在JavaScript数据结构中对象的属性访问一般用这种方式————对象.属性名

var a = {"one":1,"two":2};
alert(a.one);//1
a.one = 11;
a.one = 11;
alert(a.one);//11

JavaScript同时提供另一种来访问对象的属性————对象[“属性名”]

即a[“one”]和a.one等价

这样有什么方便的地方呢,比如我们不知道属性名,或者将属性名存放在某个变量中,属性名可以作为参数传入。比如

var arg = "one";
alert(a[arg])//等价于a["one"],等价与a.one

综上,JavaScript并没有关联数组。

java.lang.Object

混沌开天地.万物始于Object
来看这条注释@since JDK1.0
从Java这门语言诞生之初,Object类如同基石般在那里。
public class Object
12个方法
1个private,2个protected,9个public
7个native
6个final
下面我大概说一下这些方法,其中private和native以及final修饰的方法我就不说太多,这些关键字修饰的方法都是稳固的方法

public class Object
//这个就不说了
private static native void registerNatives();
static {
    registerNatives();
}
//子类不能重写的获取类型的方法,运行期得到对象类型信息
public final native Class<?> getClass();
//常用,默认返回和对象内存地址有关内容。建议重写,比如Interger类就返回value值;
public native int hashCode();
//常用,默认比较对象是否指向同一位置。建议重写,比如String类比较字符是否相同
public boolean equals(Object obj) {
    return (this == obj);
}
/*这个克隆方法是个大坑,首先The method clone for class Object performs a specific cloning operation. First, if the class of this object does not implement the interface Cloneable, then a CloneNotSupportedException is thrown. 其次对于数组this method performs a "shallow copy" of this object, not a "deep copy" operation. */
protected native Object clone() throws CloneNotSupportedException;
//常用,默认返回Class名+@+十六进制hashCode。建议重写
public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
//下面5个方法涉及线程等待和唤醒      
public final native void notify();

public final native void notifyAll();

public final native void wait(long timeout) throws InterruptedException;

public final void wait(long timeout, int nanos) throws InterruptedException {
    if (timeout < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }
    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException("nanosecond timeout value out of range");
    }
    if (nanos >= 500000 || (nanos != 0 && timeout == 0)) {
        timeout++;
    }
    wait(timeout);
}

public final void wait() throws InterruptedException {
    wait(0);
}
//垃圾回收时调用的方法,一般不需要关注。
protected void finalize() throws Throwable { }

Java的回调

Java回调

一般意义上的回调是这样的:
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
比如JavaScript里可以把函数当做参数进行传递。

function a(callback){
    //Do something else
    callback();
}
function b(){
    alert('回调');
}
a(b);

函数b显示hello字符串。而调用a函数时将b函数传递进去,由a函数自己在内部合适的时候调用b函数。这就是回调。

但是Java中不能将方法作为参数传递给其他方法(jdk1.7及之前)。在Java中对回调这个概念有自己的实现方式。
就是A类中调用B类中的某个方法do(),而方法do()中反过来调用A类中的方法callBack(),callBack()这个方法就叫回调方法。效果就是最终调用A的方法callBack(),但经历了B类中一些其他流程。回调的前提就是A对象中有一个B的实例

class B{
    public void do(A a){
        //Do something else
        a.callBack();
    }
}
class A{
    private B b = new B();
    public void callBack(){
        System.out.print("回调");
    }
    public void execute(){
        b.do(this);
    }
}

A a = new A();
a.execute("hello");

省略大部分代码。如果使用线程,就是异步回调。

这种模型在Java中最简单的体现就是观察者设计模式。在java.util包中有具体的实现,Observable类和Observer接口。

对于阿里的月饼事件一点感想

前段时间阿里有几个程序猿写脚本抢月饼被开了.
IT界引起了一些争论.总的来说就是大部分程序猿认为阿里做错了.
我并不关注谁对谁错,也不想客观的评论这件事.
我只想说一些主观的话,这件事对我的影响,以及我的想法.

1.程序猿是如此的低微和脆弱

其实程序猿和工地上搬砖的工人没有太大区别.只是我们自己把自己看的太厉害了.确实,我们用无懈可击的逻辑写着严谨的逻辑代码,沉迷于代码世界里,创造着自己所认为的艺术品.仿佛在IT领域的风口浪尖,仿佛推动着全人类的发展.
但这种自豪感和搬砖工对摩天大楼的自豪感又有什么差别呢.
没有人会记得盖起一栋大楼的千万工人.外行人也不会知道究竟是谁设计谁建造的这栋楼.他们只知道,淘宝是马云的,腾讯是马化腾的.
而搬砖工的饭碗又如此的脆弱.我也曾经认为自己的工作不可替代,如果让人顶替我,也至少需要一段时间交接工作.但现在发现真的不是这样.总有人能顶替你,甚至不需要交接工作.所以,只需要上头一句话,你的饭碗立刻没有.

2.程序猿脱离了社会

写代码写多了,思维方式总会受到影响,
正所谓有人的地方就是江湖,江湖自然有江湖的规矩,不懂的和人打交道注定要吃亏.
多提高点情商比什么都强.虽然我们并没有完全脱离社会,没有很偏执.
我的自我评价是高智商逻辑型生物,情商为0.虽是自嘲,但也差不多是这样.

[翻译]每个Java开发者都应该知道(并爱上)的8个工具

原文Eight Tools Every Java Developer Should Know (and Love)
(译文发布在码农网)

在Stormpath(一款用户管理和认证服务),我们认真对待质量和效率.任何一个优秀的工匠,仅仅拥有天赋是不够的,你在工作中还需要正确的工具.工程学不仅仅是科学,更是艺术.所以,在Stormpath,尽管我们拥有天赋,我们仍不断寻找所需的有用的工具.我们的工程师一直渴望向团队其他人分享新工具.现在,让我们向所有充满天赋的Java开发者推荐工具.

在这篇文章中,我将分享我们Java团队日常工作使用的工具,并介绍如何使用它们,分享一些实用的小技巧.

#1.Groovy
我们使用Groovy来写Java测试.为什么?因为它提供了下面这些能力:

宽松的Java语法:这是Java语法,但有些其他规则.比如分号,变量类型,访问修饰符都是可选的.后者对于测试意义重大.由于访问修饰符不是严格的,你的测试可以读取并断言类内部的状态.举个例子,我们假设下面一个类:

public class Foo {
    private String bar = null;

    public void setBar(String bar) {
        this.bar = bar;
    }
}

如果你想测试setBar(String)方法正常(即能正确修改bar的值).你可以用Groovy方便的读取变量值.Java中不允许这样的操作(在不涉及Java反射机制前提下).

@Test public void test() { 
    def foo = new Foo() 
    foo.setBar("hello") 
    Assert.isTrue(foo.bar.equals("hello"))
    //groovy 允许我们访问私有变量 bar
}

强大的断言:Groovy提供强大多样的assert,被称作power assertion statement.Groovy的强大断言能够清晰的展示验证失败时的结果.另外,它比Java更有可读性.

Assert.isTrue(foo.bar.equals("hello"));

可以用Groovy这样写:

assert foo.bar == "hello"

当断言失败时它会显示一个非常清晰的描述:

assert foo.bar == "goodbye"
       |   |   |
       |   |   false
       |   hello
       Foo@12f41634

Mocking:使用Java时,动态模拟框架(如:EasyMock,PowerMock和Mockito)非常流行.这些框架可以在Groovy下方便的使用.耶!

##2.支持REST风格
我们的后端提供REST API服务来创建和管理账户.众多SDK中,我们的Java SDK提供特定语言客户端模型做简单交互.其中一些SDK也提供网页来和后端交互,从而不用编写代码.

为了保证网络框架的互操作性,它们必须表现的一样.因此我们需要创建一系列基于HTTP的测试.这是我们的兼容性测试工具.这个项目由我们的SDK工程师维护,他们精通不止一种语言.因此我们需要跨语言测试工具.谢天谢地Rest-assured来了.

Rest-assured是Java领域特定语言(DSL domain-specific language)用来测试REST服务.它简单易用易上手,甚至对于没有用过Java的开发者也是难以置信的强大.它提供先进的特性,比如细节配置,过滤器,定制分析,跨站请求伪造(CSRF)和OAuth 2.0 .它提供非常简单的语法:given-when-then.

举个例子:让我们来看它如何校验”向/login路径发送post认证信息请求返回302状态码”:

given() .accept(ContentType.HTML) .formParam("login", account.username) .formParam("password", account.password) .when() .post(LoginRoute) .then() .statusCode(302)

你可以在我们的TCK repo中看到更多Rest-assured测试

##3.Cargo Plugin
为了让我们的Java SDK按照TCK校验,我们需要开启我们其中一个Web服务,以便测试在上面执行.讲道理的话,我们需要每次构建时自动测试.Gargo Plugin就是为此而生.
Cargo用标准的方式简单封装各种应用容器.我们使用Cargo可以毫不费力的在不同的Servlet容器(比如Jetty和Tomcat)中运行我们的代码.我们只需要在我们的pom文件中配置Cargo Maven2 Plugin来启动一个Servlet容器(Tomcat7),在测试阶段编译最近的War包.你可以在我们的Servlet插件例子中看到配置.
##4.Git
我们能讨论哪些关于Git你不了解的事情呢?想要深入了解Git,你可以看他们的About页.
我们的Java SDK团队遍布全球,而且彼此之间几乎从未坐在一起.Git保障了我们写的每一行代码.这里有一些非常棒的命令,节省了我们大量的时间:
* git mv –force foo.java Foo.java:在大小写敏感的文件系统中改变文件名是非常麻烦的.这个命令能让git意识到foo.java重命名为Foo.java
* git diff-tree –no-commit-id –name-only -r <commit_ID>: 查看所有在<commit_ID>这次提交中更改的文件.
* git diff –name-only SHA1 SHA2:列举出在SHA1和SHA2两次提交之间所有更改的文件.
* 在一个文件的历史提交记录中查询某个字符串:创建search.sh文件,粘贴下面代码:

git rev-list --all $2 | (
    while read revision; do
        git grep -F $1 $revision $2
    done
)

命令可以通过这种方式执行:sh ./search.sh string_to_search file_where_to_search

##5.GitHub
GitHub不仅仅为我们的Git项目提供托管服务,它为代码开源并让全世界都看到做出了巨大贡献.这鼓舞了人们去尝试,去交流,去练习,很大程度提高了每个人的项目质量和大家的技术水平.
GitHub允许我们跟进我们的issue.游客可以提交新需求和报告bug.他们也可以收到我们项目进展的通知.

##6.Maven
Maven已经足够出名了.所以我不会用长篇幅解释为什么我们使用Maven做构建管理.然而我可以分享几个技巧,让你的Maven更得心应手:
管理依赖 :在一个多模块的项目中,你需要在根pom.xml的<dependencyManagement>标签中定义每一个依赖.一旦你这样做,所有下层模块都可以不需要指定版本.这种管理依赖的方式(比如版本升级)可以集中处理,所有下层模块都会自动识别.比如在根pom.xml:

<dependencyManagement>
  <dependencies>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>${jjwt.version}</version>
     </dependency>
     ...
  <dependencies>
<dependencyManagement>

下层模块的pom.xml:

<dependencies>
  <dependency>
      <groupId>io.jsonwebtoken</groupId>
      <artifactId>jjwt</artifactId>  <!-- 注意这里没有指定版本 -->
  </dependency>
  ...
<dependencies>

阻止下层模块编译:在发布的时候我们需要所有下层模块一起发布,但是我们如何避免某个模块(比如example)发布呢?很简单,只需要把下面的pom文件加入到你不想发布的模块:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-deploy-plugin</artifactId>
    <version>2.7</version>
    <configuration>
        <skip>true</skip>  <!-- (敲黑板)这是重点 -->
    </configuration>
</plugin>

跳过集成测试:我们有很多集成测试需要很长时间编译.这些测试确保了后端整体运行正常.在频繁的本地部署期间,我们多次因为新功能或修复bug而更改代码.并不需要每次在本地构建的时候执行这些测试,这会拖慢开发进度.因此我们要确保我们的Java SDK只在我们的CI服务器上运行的时候执行集成测试.可以通过下面方法:
根pom.xml文件:

<properties>
    <skipITs>true</skipITs>
</properties>
...
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-failsafe-plugin</artifactId>
            <version>2.19.1</version>
            <configuration>
                <skipITs>${skipITs}</skipITs>
                <includes>
                    <include>**/*IT.*</include>
                </includes>
            </configuration>
            <executions>
               <execution>
                   <goals>
                       <goal>integration-test</goal>
                       <goal>verify</goal>
                   </goals>
               </execution>
            </executions>
        </plugin>
  </plugins>
<build>

所以你可以想象到,所有集成测试文件以IT结尾,来保证配置正常运作,比如:ApplicationIT.groovy 或 I18nIT.groovy
然后,如果我们想让集成测试运行,我们执行以下构建:mvn clean install -DskipITs=false

##7.JWT Inspector
我们的Java SDK使用JWT(JSON Web Token)通过安全可靠的方式传输数据.当我们测试排查时,我们需要分析从浏览器接收到的JWT内容.token信息可能在URL,cookie或本地储存中.JWT Inspector是一款浏览器插件,让我们可以从控制台或内置的界面解码JSON Web Token.你不需要在你的app中跟踪token信息.你只需要按一下插件的按钮,JWT Inspector会自动展示你所需要的所有信息.然后你可以复制其中任何token信息.

##8.Postman
我们重度依赖REST API请求.编写REST请求并不方便,具体语法取决于我们所用的工具,比如curl或HTTPie.两者都易读,但语法难记.通常,我们需要排查问题时,我们需要测试一些请求.当出问题时,我们无法判断原因是请求还是后端.我们浪费了大量时间来怀疑我们写的请求是否正确.
Postman让书写REST API请求变得简单.它也提供很多功能,比如保存,复用请求,生成代码(java,python,curl等等),还可以批量按序执行请求.Postman通过友好的界面帮助你构建复杂的命令.你所需要做的就是填写一个表单.简直不能再棒了.

#总结
使用正确的工具不仅仅帮助你节省时间提高效率,还能提高你作品的质量并享受日常工作.我们要时刻留心,发现并学习新的工具.一开始可能需要一些努力,但你总会意识到付出的时间是值得的.
我很想知道对你帮助最大的开发工具.可以在下面评论留言,或在tweet关注我们@gostormpath