HTML disabled 属性的一个坑

一天我在一个页面里写下如下的代码:

<div disabled></div>

并在 CSS 中设置:#xx:disabled { background-color: red; }

可是,此 CSS 并没有生效。

后来考虑到,disabled 这个属性是不是对 div 无效?经过查询,果然 disabled 属性是针对表单的,于是果断把 div 改为 button,发现依然无效。

果断在 W3School 查找,发现 disabled 属性只对 input 元素有效

C# 与 Java 语言机制与语法的异同

本文是 从 C# 到 Java 系列的文章。此系列文章是为了方便从 C# 转到 Java 平台的工程师快速学习新的语言和平台。整个系列的目录请点击此处浏览

这篇文章从基础的语言机制、语法层面来分析 C# 和 Java 的不同。部分与虚拟机(CLR | JVM)有关的不同会放在相关的另一篇里,详见总目录。

命名规范

Java 中的方法和变量,无论是否为public,都要以小写字母开头,其余与 C# 大体相当。

那些简单的同义词或近义词

下面的词汇,在 JavaC# 中是 同义近义 的:

  • C# 中获取一个对象的类型使用 typeof 关键词,Java 中使用 .class,如 String.class;C# 也可以使用对象实例的 GetType 方法,Java 中对应的是 getClass 方法。参考链接
  • 代表一个对象的类型的类型,C# 为 Type 类型,Java 则为 Class 类型。参考链接
  • C# 中的 const (编译时常量)和 readonly(运行时常量)在 Java 中都是 final,同时还代表了一个类不能被继承(等同于 C#sealed)。
  • Java 中的 native 关键字(声明使用 native 实现的代码)类似于 C# 中的 extern(当声明非托管的外部方法,如动态链接库时使用)。
  • stringString:在 Java 只有后者,在 C# 中前后两者是同义词。
  • Java 中的 synchronized 等同于 C# 语句块中的 lock,以及 方法体上的特性MethodImplOptions.Synchronized
  • Java 中的 transient (短暂)等同于 C# 的 NonSerializableAttribute,代表在序列化中跳过。
  • Java 的 ... 操作符等同于 C# 的 params。例如:String... args,则 args 为一个所有参数组成的数组。

值类型与装箱

Java 的值类型(Value Types)仅限于原生类型,如 intboolen 等。Java 不允许自定义值类型,即 Java 不存在 C# 中的 struct

值类型 的作用可以从以下几个方面来考虑:

  • 性能:C# 中的 struct 类型有助于减少 GC、减少内存使用、提高性能,但 Java 强大的虚拟机 JVM 通过优化部分缓解了这个问题。
  • 语义:原生类型无法为 null,且通过传值方式赋值。

但是,由于 C# 中的值类型通过传值类型赋值造成了意外错误的可能(例如用类似类的方式在杯传递后的值类型中修改成员,试图影响到原有的值),使用值类型要警惕。

值类型在 Java 中不继承于 Object,但 C# 中却继承,这是通过自动装箱实现的。Java 的值类型可以通过装箱变为类实例,比如:

int => Integer
float => Float
byte => Byte

Java 具体的数据类型方面的内容,可以参考本系列文章中的另一篇:数据结构(文章末尾有目录导航)。

泛型

Java 支持泛型,但其支持的方式和 .NET 差别比较大。Java 的泛型使用 类型擦除 实现。所以,你虽然可以设置某个方法或类有一个类型参数 T,但是你在其实现内部却无法获知与该类型的任何信息,解决方法就是使用一个补偿的 class 类型(类似于 C# 中的 Type 类型)参数手工传递数据类型。

有关 Java 泛型的类型擦除,请参考链接

委托,事件

C# 中的委托(delegate)用于指向固定某个方法原型(一系列参数、某种返回值)的指针(姑且这么叫吧)。Java 中没有 delegate 的概念。

Java 中一般通过接口来实现类似委托的效果。

例如,C# 中假如有下面的定义:

 // 定义委托
public delegate void onClickListener(View v);

// 定义委托的实现
public void listener(View v) {
  // foo
}

// 使用委托
onClickListener = listener;
onClickListener(this); 

在 Java 中可以这样实现。先定义一个接口:

public interface OnClickListener {
    void onClick(View v);
}

然后,使用这个接口来存储一个匿名类,其中带有具体的实现,例如:

OnClickListener listener = new OnClickListener() {
    @Override
        void onClick(View v) {
            // 在此书写代码
        }
};

// 调用接口
listener.onClick(someView);

除了使用匿名类,也可以让某个命名类继承此接口:

public class TestClass implements OnClickListener {
        void OnClick(View v) {
            // 在此书写代码
        }
        
        void init() {
            OnClickListener listener = this;
                
                // 调用接口
                listener.onClick(someView);
        }
}

C# 支持事件(event),其内部是通过委托来实现的。Java 中没有事件的概念。

一般来说,用于事件或委托的情况,Java 会使用接口来实现。

例如,C# 中的事件:

public event EventHandler SomeEvent; // 事件的发出者

// 事件监听
someInstance.SomeEvent += (sender, args) => { /* xxx */ };   

大约等价于 Java 中的方法:

// 先定义接口
public interface SomeEventListener {
    void onEvent(int arg);
}

// 搞一个东西用来存储接口的具体实现对象
private ArrayList<SomeEventListener> someEventListeners = new ArrayList<>();

// 在要产生事件的类中,添加一对方法用于监听或取消监听
public void addSomeEventListener(SomeEventListener listener) {
    someEventListeners.add(listener);
}

public void removeSomeEventListener(SomeEventListener listener) {
    someEventListeners.remove(listener);
}

// 触发事件
for (int i = 0; i < someEventListeners,size(); ++i) {
    someEventListeners.get(i).onEvent(5);
}

// 事件监听者,使用 Java8 中才支持的 Lambd 表达式
someInstance.addSomeEventListener(() -> {  /* xxx */ });

// 事件监听者,使用 Java7 以前版本的 匿名类
someInstance.addSomeEventListener(new SomeEventListener() {
    @Override
        void onEvent(int arg) {
            // 代码逻辑
        }
});

也可以参考这篇文章

本文是 从 C# 到 Java 系列的文章。此系列文章是为了方便从 C# 转到 Java 平台的工程师快速学习新的语言和平台。整个系列的目录请点击此处浏览

为什么要从 C# 转为 Java:浅谈 .NET 与 Java 生态

本文是 从 C# 到 Java 系列的第一篇文章。整个系列的目录请点击此处浏览

C# 是 .NET 平台上最重要的一门语言,Java 是世界上使用最广的语言之一。这两门语言有着太多的恩怨情仇,也有很多相似之处。C# 在最初创立的时候,或多或少借鉴了 Java 语言的优秀特性,但后期发展又和 Java 走上了不同的道路。

那么,为什么你要从一门语言,转换为另一门很相似的语言呢?本文主要谈谈,为什么在某些情况下,你可能需要,或者愿意从基于 .NET 的 C# 转移到基于 JVM 的 Java 语言平台。

统一开发平台,重用现有资产

  • 某些类型的开发不得不用 Java 开发,或者大多数人都在用 Java 开发。如 Android 开发,Android 的开发技术栈全部构建于 JVM 之上;所以整个团队的开发语言只能迁就于上述原因来选用 Java;
  • 很多团队或公司中,有大量现存的基于 Java 的资产(代码、模块、应用),统一开发语言和平台可以降低团队磨合、学习成本,重用现有代码资产。而统一语言时显然无法选择 C#,因为 Java 适用面更广(即很少有 Java 可做而 C# 无法做的领域或平台)。

减少技术风险,方便技术学习

  • Java 的历史更长,开发人员更多,社区也更大,如果在开发上遇到问题,有很多成熟的解决方案可以参考(俗称的Google之);
  • 开发特定领域的程序时,Java 可借鉴的代码和框架更多,避免遇到技术死角;大多数领域的开发已经在 Java 中有成功的案例,避免技术上潜在的风险。
  • 某些第三方产品或接口,官方优先支持 Java 或只支持 Java,使用其他平台需要额外开发或进行封装,导致开发效率和性能的问题。

减少开支

  • 靠谱的 Java 程序员更好招聘,可以减少用于招聘的人力资源成本;原因有二。一是 C# 确实比 Java 对开发者更贴心,所以导致了学习成本更低,造成了 C# 小白丧失了继续学习的进取心,客观上降低了 C# 开发者的平均水平;二是从绝对数量上来说,C# 开发者也少于 Java 开发者(如下图)。
  • 基于 .NET 平台的开发工具和相关产品普遍需要付费;基于 Java 的开发工具和相关产品免费的更多。

下图为 2016年11月,在全球范围内 C# 与 Java 语言的流行度对比,Java 排名第一,C# 排第四。

C# 与 Java 流行度对比

基于 Java 开发将拥抱更广泛的生态

  • 如上图所示,Java 长期在开发语言排行榜中流行程度排名第一,所以拥有更好的生态,即有成千上万的成熟类库大量的技术资料很多的现成代码
  • Java 更偏向于基于 Linux/Unix 平台的开发,而 .NET 在早期太多地依赖于 Windows,后期 .NET 虽然开源且跨平台,但已为时略晚。在互联网相关行业,Linux 及其生态圈比 Windows 繁荣得多。
  • Java 早已不是一个开发语言,而是一整个生态圈
  • Java 在语法、平台技术上、开发效率上并非最好的,但却是够用的,且有出色的平衡性。虽然 Java 在哪个方面几乎都不是最强的,但却都说得过去。

说实话,我个人更偏爱 .NET 技术,其炫酷、性能优异、免费且跨平台、开发效率高,但是,基于以上原因,有时 Java 是更务实的选择。

如果你准备好了基于 Java,而不是 C# 开启你的下一段代码,就让我们一起来学习吧!

本文是 从 C# 到 Java 系列的第一篇文章。整个系列的目录请点击此处浏览

从 C# 到 Java:总目录

引言

为了方便以前使用 C#(.NET),由于某些原因,需要学习 Java (JVM),或开发基于其的应用的人(比如开发 Android 应用),我打算写一篇系列文章,详细地描述中间可能遇到地问题、学习路径等。方便 .NET 开发人员快速转为 Java 开发人员。

具体来说,主要会通过对比和举例来行文。在过程中,不但会讲 Java,也会把 C#.NET) 的一些内容讲一讲,算是起到两种语言甚至是平台的对比与深入。

下面是总目录,没写好的部分不可点击,有链接的部分才可以阅读。

总目录

为什么要从 C# 转为 Java:浅谈 .NET 与 Java 生态
C# 与 Java 语言机制与语法的异同
.NET 与 Java 数据结构异同
.NET 与 Java 核心类库的主要区别
CLR 与 JVM 对比
从 C# 到 Java:Java 包管理、主要框架与测试
从 C# 到 Java:开发范式与哲学

Javascript 中的 undefined 与 未定义(not defined)

在 Javascript 中,有一种致命错误非常常见,即 ReferenceError:xxx is not defined. 但是,有时候当一个值从未被赋值的时候,并不会出错,而是返回 undefined,有必要对这两者进行梳理。

undefined

undefined 表达的含义有三种情况:

  1. 这个变量存在,但是并没有给予任何值。
  2. 一种普通的数据类型和一种值。你可以手工将任何变量赋值为 undefined,此时其没有特别含义。
  3. 一个存在的对象中的一个不存在(没有声明)的值,会被认为是 undefined。

对这三种情况进行归纳,undefined 可以如下总结:

  • 它是一种数据类型,也是一种值。
  • 有这种值的时候,要么它本身被赋值,要么本身存在,要么其所在对象存在。完全不存在的变量不会是 undefined(注意,此处还有坑,往后看)。
    下面举几个例子。

一个声明但没有赋值的变量为 undefined

    var p;
    alert(p); // undefined

一个不存在的对象成员为 undefined

    var p = { };
    alert(p.a); // undefined

如果 p 还有一个成员 b,其值被手工赋值为 undefined,如何区分不存在的成员 a 和存在的成员 b?可以使用 Object.prototype.HasOwnProperty() 方法。

一个手工赋值的 undefined

    var p = 5;
    p = undefined;
    alert(p); // undefined

从上面的三个例子可以清楚看出 undefined 的一些情况。undefined 的优点在于正常使用有此值的变量不会抛出异常。

未定义(not defined)

一个 未定义 (not defined) 的变量是完全没有任何声明的变量。这样的变量在使用时会直接抛出致命错误。但是,如果使用 typeof 来判断这样的变量,不但不会出错,而且竟然会返回 undefined,这使得无法使用 typeof 来区分这两种情况。

1 2 3 10