`
JeffreyZhao
  • 浏览: 11995 次
  • 性别: Icon_minigender_1
  • 来自: 上海
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

Why Java Sucks and C# Rocks(2):基础类型与面向对象

阅读更多

既然已经谈过这次语言比较的意义与目的,而完整的幻灯片和录音也已经放出,那么接下来自然是详细讨论了。在这篇文章中,我会对两个语言的基本特征进行简单描述,并主要讨论两者对于基础类型的处理方式。在我看来,Java语言对于基础类型的处理方式,并不如C#中值类型般妥当。如果您有任何觉得不妥或是想要补充的意见,请不吝回复。由于C# 1.0发布于2002年,因此本文内容将基于Java 1.4及C# 1.0的情况。

Java语言简单描述

Java既是一个完整的平台,也是一门语言。Java语言是1995年由James GoslingSun Microsystems公司设计,作为Java平台的组成部分之一的语言。Java平台除了语言之外,还有两个组成部分,虚拟机(JVM)和类库。不过在这一系列文章中,我会将关注里放在Java语言这个单独的方面,偶尔会谈到一点JVM,而对于类库方面则几乎不会涉及。

Java语言参考了C语言和C++的设计,因此在代码整体风格上与它们比较类似。不过与C++相比,Java语言设计的更为小巧,简单和可靠。Java的类型分为两种:类(class)和基本类型(primitive type),并没有C++中的struct和union类型。同时,Java还提供了boolean类型,并对布尔类型的定义和使用作出了限制。此外,Java中也不允许开发人员进行运算符重载,但提供如synchronize等进行并发控制的语言特性。

Java语言设计的一大目标是可靠性,因此在Java中,部分元素的抽象级别被提高了,例如数组在Java中是预定义类的实例,在C++中就不是。此外,例如在数组访问中会进行下标范围检测,同时也去除了指针等不安全的语言特性。虽然Java中的“引用”也有些“指针”的意味,但更为安全,例如程序员不可以对指针进行算术运算,这样便避免了一些容易出错的做法。

在面向对象类型系统的设计中,Java不允许C++中的多重继承,因为许多人认为多重继承所带了许多复杂性和混乱,可谓弊大于利。不过Java允许开发人员定义“接口”,即一种“契约”而不包含实现,这在一定程度上也可以带来部分多重继承的优点。

总体而言,Java语言去除了C++中大量的复杂或是不安全的特性,这使的Java成为了一门灵活而强大,同时又更为小巧,简单和可靠的语言。从现在的角度看,Java语言大大降低了C++本身所带来的复杂度,让编程工作变的更为简单,具有很高的历史意义。

C#语言简单描述

C#语言是Anders Hejlsberg为微软.NET平台设计的一门语言。与Java语言不同的是,C#的定位只是.NET平台上各种语言中的一种——尽管如此,从现在来看,.NET平台和Java平台虽然起步不同,但可谓殊途同归。与Java平台比较类似,.NET平台除了多种语言之外,也有对应的虚拟机(CLR)及基础类库(BCL)。

C#的语言设计同样是基于C和C++的,但是也包括了一些Delphi和VB的思想。许多人觉得Java语言相比C++的优越之处在于“简单”,不过似乎C#的设计人员认为Java语言简化的有些太过了,因此C#将指针(受到一定限制)、struct、enum、运算符重载及goto等语言特性包含在C#语言中。不过,C#的设计人员同时也改进了这些特性,例如C#中的enum类型不能同整数类型进行隐式转换。同样,在C#的struct中,我们也可以为它定义构造函数和成员,并可以实现接口。

C#中还提供了一种数据类型“委托(Delegate)”,它可以被视为是一种类型安全的函数指针。一个委托也是一个对象,它明确定义了函数的签名,可以用来保存一个包含了调用时所需的上下文的函数。在.NET程序中,一个回调函数往往会使用委托进行表示,而在Java中,则一般会使用接口。

C#还提供了一些方便开发的特性,例如foreach,using,以及使用params定义可变长数组等等。此外,C#还提供了事件(event)及属性(property)两种对象的成员类型。属性可以简单理解为带有读写器(也可以只读或只写)的字段。Java语言虽然不包含属性,但很早便形成了一个约定,会将getXyz/setXyz方法作为属性处理,许多开发工具和类库都会以这种方式进行处理。

C#语言的初衷是要成为优越于C++和Java的通用程序设计语言。经过多年的发展,C#在语言设计方面的确遥遥领先于Java语言,这也是我写这一系列文章的事实依据。

Java是个纯面向对象语言吗?

Java在诞生于面向对象理论在业界愈发普及和流行的时期(著名的GoF设计模式一书便出版于1994年),在当时Java声称自己是个“纯面向对象语言”,但事实果真如此吗?我不这么认为,因为有很明显的一点是,Java语言中的基础类型不是对象。

在Java中定义了一系列基础数据类型,例如int,double,boolean等等。这些类型不是对象,它们不包含成员,也不继承于公共基类Object,因此Java并不是一门纯面向对象语言。

不过,其实我关注的并不是“纯面向对象”这个称号,我更关注Java语言在设计时是如何对待这些类型的。从代码上看,这个特点的确造成了诸多不便:

ArrayList list = new ArrayList();
list.add(5); // cannot compile
int i = (int)list.get(0); // cannot compile

int hash = 3.hashCode(); // cannot compile

上面这行代码有三处会编译不通过。ArrayList是个用于保存Object类型对象的容器,可以存放任何类型的对象。不过由于int类型的数值5不是对象,因此它无法被添加到ArrayList中。同样,在通过get方法获取到一个Object类型的对象后,我们也无法将其转换成int类型。自然,int类型没有成员,因此我们也无法通过它调用定义在Object类型上的hashCode方法。

直至Java 1.4,开发人员都必须编写这样的代码:

ArrayList list = new ArrayList();
list.add(Integer.valueOf(5));
int i = ((Integer)list.get(0)).intValue();

int hash = Integer.valueOf(3).hashCode();

在Java类库中也为每个基础类型分别指定了封装类(wrapper class),当遇到一些需要和Object进行互操作的时候,便可以把基础类型包装为一个对象。这些对象继承于Object类型,自然能够被添加到ArrayList中,也拥有hashCode等定义在基类中的方法。只是,在获取到Object对象并转换成封装对象时,还需要调用对象上的某个方法(如上面的intValue方法)才能重新获取到基础类型。

C#中的值类型

在此期间微软发布了.NET平台和C#语言。在C#语言中并没有所谓的“基础类型”,或者说C#的“基础类型”是作为.NET中的struct类型统一对待的。在.NET框架中定义了一系列struct类型,如Int32,Boolean,Double等等,在C#语言中使用关键字int,bool,double与之对应,这便有了一些“基础类型”的意味。自然,在C#代码中我们也可以直接使用那些类型的名称,完全等价。

开发人员可以在程序中定义自己的struct类型,并包含构造函数,方法或是属性等等,并统一继承于ValueType类型(值类型),而ValueType也是统一基类Object的子类。因此,在C#中那些“基础类型”也拥有Object类及自己的成员,并可以直接应用在需要Object的地方(即隐式转换)。因此,在C#中我们可以直接编写这样的代码:

ArrayList list = new ArrayList();
list.Add(5);
int i = (int)list[0];

int hash = 3.GetHashCode();

相信看了这几行代码您就能明白了。可以看出,在C#中,值类型和普通的类(也被称为引用类型)在使用上并没有任何区别。

当然,既然被称为“值类型”,它自然和普通的引用类型有所区别。首先,在.NET中值类型在赋值时是“整体拷贝”而引用类型只是复制一个引用。更重要的是,值类型是分配在方法的调用栈上,而引用类型则是分配在托管堆上。这意味着前者在当前方法退出后会被自动释放,而后者则必须等待GC运行时将无用的对象消除。

换句话说,值类型不会对GC造成压力(除非进行了装箱),这点很重要。在某些场景中,例如在并行计算时,假如每个线程临时对象创建地过于频繁,则可能导致GC频率加大。而GC在启动时会暂停运行中的所有线程,因此并行计算最终的瓶颈可能就落在了单线程的GC上——此时您投入再多的CPU等运算资源也无济于事。解决这个问题一般有两种办法,首先是启用并行GC,则为每个CPU分配一个独立的托管堆。在执行时,对象会分配在当前CPU的托管堆上,每个托管堆也有一个线程负责GC操作,这样GC能力也会随着计算能力加大而提高,因此不会成为性能瓶颈。另一种,有时候也可能是更为合适的做法,便是将创建的临时对象设定为值类型,这样一切便是在调用栈上的读写操作,便不会对GC造成压力。

在.NET平台中,一旦将一个值类型的对象用作引用类型时(即转化为Object类型或接口),运行时便会对它进行“装箱”:此时运行时会在堆上创建一个对象,并将值类型的内容复制到对象内部。将一个装箱后的对象转化为值类型时,则会将托管堆上的对象内容复制到方法的调用栈上,这便是所谓的“拆箱”。装箱和拆箱在.NET中是由运行时负责的,不需要特定的封装类,支持任意值类型。

在Java中,开发人员无法自定义值类型,因此所有的对象都是分配在托管堆上。不过这些倒也是和平台密切相关的内容,这里便只作一提吧,毕竟我们的目标主要还是在语言方面。

Java 1.5中的自动装箱/拆箱

Java语言基础类型和封装类型之间的这种转化方式,从1995年Java语言出现开始,一直保持到2004年Java 1.5出现才有所改变,将近十年时间。那么,Java语言究竟是因为缺少竞争对手而不思进取,还是因为没有比较就体会不到麻烦呢?无论怎样,我相信C#在这方面对Java语言产生的影响是毋庸置疑的。

不管怎么样,在Java 1.5中引入了一个新特性:自动装箱/拆箱(auto boxing/unboxing)。此时,我们便可以编写这样的代码了:

ArrayList list = new ArrayList();
list.add(5); // auto boxing
int i = (Integer)list.get(0); // auto unboxing

在int值5用在Object参数的时候,Java语言的编译器将自动生成创建Integer对象的bytecode。同样,将Integer类型的对象赋值给基础类型int的时候,Java语言的编译器也会自动生成intValue等方法的调用。那么,它和C#中的装箱和拆箱有什么不同呢?自然,区别之一在于C#的装箱和拆箱是.NET平台已有的功能,C#编译器只要直接使用即可,而Java的自动装箱和拆箱完全是编译器的工作。但我倒认为,这个区别并不是我这里特别关注的。

毕竟我现在关注的是语言,也就是通过代码本身所表现出来的,尤其是可以体现出两种语言在设计理念上有所不同的区别。

如果我们仔细观察代码的话,就可以发现,Java编译器其实是将Integer对象与int值互转,例如在上面的第3行代码中,我们先将Object对象转化成Integer类型,然后再隐式地转化至int基本类型。而在C#中,int类型是直接和Object类型相互转化的。我认为,Java的这种做法,表示Java的设计者依然不希望开发人员将int等基本类型看作是一种对象,他们只是让编译器可以帮助开发人员少些一些代码而已。换句话说,Java的设计者认为,int可以看作是Integer对象,boolean是Boolean对象,但是int和boolean仍然是基础类型,而不是对象。

下面的这行代码可能更加能够直接说明这个问题:

int hash = 3.hashCode(); // cannot compile

在Java中,这行代码是无法编译通过的,因为Java编译器并不会将int自动视作Integer对象,基础类型在这里依然是基础类型,不是对象。

在之前的讨论过程中,有朋友说,Java在这里不做自动装箱,是因为要在bytecode层面上保持与之前兼容。我不同意这个说法,因为我想象不到实现如C#这样的自动装箱和拆箱会破坏bytecode的兼容性,Java编译器完全也可以在语言级别将int类型和Integer类型等同起来,所以在这方面我认为完全是语言设计理念上的区别。

说实话,我不喜欢Java的思路,我更认同C#这种更为“面向对象”的设计方式。

相关文章

20
25
分享到:
评论
9 楼 RednaxelaFX 2010-04-27  
来抓点虫……
引用
更重要的是,值类型是分配在方法的调用栈上,而引用类型则是分配在托管堆上。

这里变为“值类型的局部变量的值”与“引用类型的变量所指向的值”会比较合适。毕竟可以这样:
struct Foo {
  public int value;
}

class Bar {
  public Foo Value { get; set; }
}

于是Bar里的Foo仍然是值类型的Foo,但它的分配是在堆上的。
Eric Lippert之前专门写过几帖强调“在栈上分配”只是实现细节而不是值类型的本质特征

引用
自然,区别之一在于C#的装箱和拆箱是.NET平台已有的功能,C#编译器只要直接使用即可,而Java的自动装箱和拆箱完全是编译器的工作。

其实这点对C#与Java的编译器来说是一样的,它们都需要通过类型检查发现需要装箱与拆箱,并且都可以用底下的“平台”提供的现成功能:CLR上用box/unbox指令,Java上用valueOf()/xxxValue()调用,对编译器编写者来说区别确实不大。

Hmm...有了MethodHandle之后Java好歹也能在反射API之外用对象来指代方法了,好歹又朝“面向对象”走近了一步。

老赵这篇想说的一个重点就是“统一性”,对吧?值类型的处理方面看,C#确实比Java做得好些。要举例子的话还有不少出现在平时代码里的东西把我恶心到的,
Integer i = new Integer(1000);
Integer j = new Integer(1000);
boolean what = (i == j) || (i < j) || (i > j);

然后你会看到what是false。这种在写Comparator<Integer>的时候最需要小心,很容易掉陷阱里去。
还有一个:
Integer i = 1;
Long l = i; // 编译失败

这也让我很郁闷……由于Integer与Long之间没有转型关系,所以在反射调用的时候还得特别小心。我们这边自用的commons库里的反射调用代码就受其所赐而不得不做繁琐的处理。

但是别的一些方面,C#的“统一性”也有些让人不爽的地方。
例如说方法与属性的统一性。在Eiffel里,feature(可以大致看成C#/Java的方法)的语法就是统一的。如果用C#来举例的话,大概会是这样:
public interface IFoo {
    string Name;
}

然后可以有多种实现方式:
class Foo : IFoo {
    public string Name;
}

class Bar : IFoo {
    public string Name { get; private set; }
}

class Baz : IFoo {
    public string Name() { return "Baz" }
}

调用的时候统一用foo.Name即可。
C#最初借鉴Java的时候把covariant array借了过来也是一大败笔。诶。

前面NS说……
night_stalker 写道
另外前几天 FX 的巨型 ppt 里也提到 java 类在 hotspot vm 里的表示是有对象 prototype 的影子的。

我是说HotSpot内Java类也是对象(klass),不过我没说prototype……

至于Scala摆脱不了Java的影响,更多时候是无奈——为了能平滑兼容Java的API,为了能让从Java一侧传入的对象能直接用。
原本Martin Odersky鼓捣出Pizza,Sun请他去做GJ的原型实现,后来他甚是不满GJ把他所想的泛型做坏了。后来制作了Scala。这对javac的怨念…… XDD

Neal Gafter...不管是什么原因都好,他现在在微软了。BGGA方案现在也还是有一线曙光的,只不过比起原本的方案要精简掉许多。

要说鼓捣javac,Mono的C#编译器也经常是社区里大家热衷于鼓捣的对象。大家也没少鼓捣出些诡异的东西,像是在C#里内嵌MSIL、语法级tuple支持之类。javac开源前大家也就是鼓捣鼓捣Jikes和ECJ或者自己搞,也是差不多。考虑到.NET平台与Java平台出生日期的差距……hmm不过倒也不一定要等微软的C#编译器开源,反正下一代C#把编译器服务暴露出来就已经可以很方便的各种hack了。

如果大家都因为缺轮子而不得不去造的话那确实挺锻炼的……在一个既能舒服的用轮子又能轻松的造轮子的地方感觉更爽一些 >_<
8 楼 night_stalker 2010-04-27  
下面纯举几个 Ruby 的语法优于 C# 的地方。尽量不涉及其它东西 ……

1. Ruby 支持模式匹配(注意并行赋值不是语法糖!强调 n 次了都没人理 ……)
x, *xs = [1, 2, 3, 4]
x #=> 1
xs #=> [2, 3, 4]

Point = Struct.new :x, :y
x1, y1 = Point[12, 4]
x1 #=> 12
y1 #=> 4


2. Ruby 可以在 each、map 和 inject(相当于 F# 的 fold) 中用 break、next(相当于 java 的 continue) 和 redo 。 rescue 里面也可以 redo,管理带事务的逻辑时清晰很多。另外,each 比 ForEach 好看 100 倍 ……

3. Ruby 的类是 first class 对象。这是非常非常重要的一点 —— 因为类所附带的信息都在此对象上,所以可以直接干掉静态成员、泛型和 Attribute 这三大语法元素。
这也是 Ruby 写起简单,而且反射 API 非常友好的原因之一。

带来的一些语法一致性的好处如:
Ruby 不需要 instanceof 关键字,用 object.is_a?(Klass) 或者 object.class == Klass。
Ruby 也不需要 new 操作符,直接 Klass.new 即可。

题外1: 本来 smalltalk 就已经有“类是对象”这个特征了,只是派生 C++ 语法的语言都忘记了这一点 …… 另外前几天 FX 的巨型 ppt 里也提到 java 类在 hotspot vm 里的表示是有对象 prototype 的影子的。

题外2: Scala 在这上面做了点折衷,可以写 Companion Singleton Object,消灭了静态成员。但它没做到最后一步,类型信息依然在 Class 对象中,所以还需要泛型和 Annotation ……

4. Ruby 里写整数可以带下划线让代码更清晰,如 1234_5678。
Ruby 原生支持任意长整数,不需要用 BigInteger 或者 IntX 写一长串,标准库带矩阵运算和素数扩展。

5. C# 和 Java 的字符串只有一种编码:UTF-16,其它编码的字符串都是 byte array,如果要像字符串那样对待它们,需要先进行转换,极大地降低了运行效率 …… 如果要保持高效,就要放弃使用一些字符串相关的 API,自己手动去做,极大地降低了编写效率。
Ruby 字符串可以是任意编码,而且有丰富好用的字符串操作 API 和原生正则的支持。

6. Ruby 的注释是 #,可以用 bang comment,在 linux 和 mac 下源文件可直接执行。

7. Ruby 的 if ... else ... end 是表达式, begin ... end (相当于 C# 的匿名块 {})也是表达式。很容易提出一些重复的东西:

int v;
if (cond) {
  ...
  v = 1;
} else {
  ...
  v = 2;
}
// 坏味道:v 出现了 3 次(那些分号就不和你计较了)


v = if cond
    ...
    1
  else
    ...
    2
  end
# 清爽~ 和 Haskell,Scala 一样


题外:当然很多人不喜欢写 end,这是个括号的取舍问题 —— end 是一种括号来的 …… Python 没有块括号,但函数调用就不能像 Ruby 那样省略括号了。
我小时候玩 QBasic 和 VB 的所以很习惯 end …… orz

8. Ruby 没有 namespace / package,用 module 来管理可见性 —— 因为 module 是可以 re-open 的。用同一种语法元素同时实现了命名空间管理和 mix in,一石二鸟,一举两得 …… 但是有得必有失,不能像 C# 那样做 scoped mixin。

9. 其实库是 Ruby 的优势之一 …… Ruby 的代码重用率是非常高的,一些东西经常会在连作者都想不到的神奇地方被人重用 …… orz。 有 rubygems 这个强大的包管理系统,就不用经常上 codeproject 拷代码了 ……
7 楼 night_stalker 2010-04-27  
Ruby 跟 C# 比,就和 C# 跟 Java 比差不多。
6 楼 JeffreyZhao 2010-04-27  
五月天 写道
我不知道写这篇文章的作者的用意如何,不过显示有点偏重c#,可能是学c#的吧?
其实语言没什么可比较的,关键在于运用在什么地方,java也好,C#也好,只要能做出好的产品,都是好的。java和c#不都还在发展吗?java7出来后说不定会比c#更实用呢?且现在的ruby语言比上面的两种语言更人性化。所以没必要在语言级方面作什么比较~!
我也纳闷了:怎么现在老是有这样的文章出现。作者就没能写点其它更有意义的东西吗?

我觉得这个话题很有意义呢,国内从来没有过靠谱的比较,于是就由我来了。其实C#比Java的优势不在于功能多,而在于强大,生产力高,提供更好的编程范式及思维。Ruby的话,我不觉得比C#有多少优势啊,希望有人可以比较一下C#语言和Ruby语言的生产力,呵呵。

对了,我这次文章会一直比到最新的C# 4和Java 7的,结果就等我继续写咯。
5 楼 ZHH2009 2010-04-27  
JeffreyZhao 写道
@ZHH2009

其他不多说了都说过很多遍了,我现在想说的是……

微软给技术人员带来了很多东西啊,就拿最新的来说,IronXxx,DLR,F#,Reactive Framework,Parallel Library等等。而且你说Java影响了C#,Scala的设计,其实新的语言比如C#,Scala,Groovy,F#等等基本都在互相影响,噢,还有Haskell被人参考了很多。.NET虽没有Java平台开放,但是可能也比您想象中来的开放吧,呵呵。

当然,这个技术社区可能的确不太争气,但是这些技术的质量还是相当好滴。Java平台不该死,但Java语言的确该死了……好吧,我是指退出历史舞台,放到神坛上供着就可以了,大家都用Scala吧,活活……



呵呵,不多说了,微软的东西我大多只听过名词,没啥研究,也谈不出好坏,
C#、ASP.NET这类语言还是蹲书店时看看书瞄过的。

你很看好Scala,不过我就不像你那么乐观了
(记得在08年就跟草原大叔聊过几句,差点跟他搞Scala的NetBeans插件去了)
Scala过于复杂,VB、Java流行了这么久,就因为他们简单,
大众开发人员并不都是像你这类精英的,
静观其变吧,当然,或许你以后能左右Scala、Java、C#的发展也不一定。
4 楼 五月天 2010-04-27  
我不知道写这篇文章的作者的用意如何,不过显示有点偏重c#,可能是学c#的吧?
其实语言没什么可比较的,关键在于运用在什么地方,java也好,C#也好,只要能做出好的产品,都是好的。java和c#不都还在发展吗?java7出来后说不定会比c#更实用呢?且现在的ruby语言比上面的两种语言更人性化。所以没必要在语言级方面作什么比较~!
我也纳闷了:怎么现在老是有这样的文章出现。作者就没能写点其它更有意义的东西吗?
3 楼 JeffreyZhao 2010-04-27  
@ZHH2009

其他不多说了都说过很多遍了,我现在想说的是……

微软给技术人员带来了很多东西啊,就拿最新的来说,IronXxx,DLR,F#,Reactive Framework,Parallel Library等等。而且你说Java影响了C#,Scala的设计,其实新的语言比如C#,Scala,Groovy,F#等等基本都在互相影响,噢,还有Haskell被人参考了很多。.NET虽没有Java平台开放,但是可能也比您想象中来的开放吧,呵呵。

当然,这个技术社区可能的确不太争气,但是这些技术的质量还是相当好滴。Java平台不该死,但Java语言的确该死了……好吧,我是指退出历史舞台,放到神坛上供着就可以了,大家都用Scala吧,活活……
2 楼 ZHH2009 2010-04-27  
恕我冒昧,我来坐坐板凳吧。
cnblogs那边很火,JavaEye不一定喜欢这种东西的。

光是讨论语言特性估计没太多人想聊,除非你说的话很有火药味,就像CSDN中常见到的那样。。。

C#的语言特性肯定比Java丰富,这没什么好置疑的了,

但是不能因为Java语言特性少就认为“Java将死”,
C#是微软在玩,是Anders Hejlsberg在玩,而Java是整个JCP在玩,
James Gosling已经好多年没写Javac编译器代码了。

只要微软高兴、只要Anders Hejlsberg高兴要往C#中加入新特性很容易,
但是Java就很难了,你不见Neal Gafter好不容易实现了那个叫啥闭包方案却不能引入Java 7,不知道Neal Gafter装了多少苦水。

但是Java是开放的啊,Javac、HotSpot都开放了啊,
这就是吸引技术人员的最大优点,你觉得Java语言特性不够用,
去鼓捣Javac啊,比如我就玩了一下Javac,整出了一点小东西。
比如Scala之父 Martin Odersky也参与过Javac的开发,
Java5中的范型原型最初好像就是Martin Odersky鼓捣出来的。

Java的开放才是最有价值的,你可以任意发挥你的创新能力,C#最初不就是抄Java的么,
Martin Odersky也受了Java的影响,Scala自然也摆脱不了Java。

反观微软呢,微软带给技术人员什么?
cnblogs我也偶尔去看看,微软阵营和Java阵营差异性很大,
可以简单的用一句话描述两个阵营的爱好:
前者对怎么使用工具有兴趣,后者对怎么制造工具有兴趣。
1 楼 JeffreyZhao 2010-04-27  
呃,只见顶和踩,不见评论的样子……

相关推荐

    大师品软件_Why Software Sucks

    找到这也知道是书讲什么了,呵呵~~ 与该书Djvu格式相比改进了: 1、添加中文书签,书签翻译来源“csdn图书品读”提供的目录 2、导出pdf时压缩了图片,文件较小

    信息安全_数据安全_Why_the_role_of_CISO_sucks_and_w.pdf

    信息安全_数据安全_Why_the_role_of_CISO_sucks_and_w 信息安全研究 金融安全 安全人才 安全对抗 法律法规

    Why.Software.Sucks

    and got behind the concept of a book for the users of computers, not the programmers that they usually deal with. Instead of, "That's not what we do here," they stepped up and said, "Hey, cool, look...

    sucks-rocks:用于收集对小代码片段的反馈的 Web 应用程序

    很烂 用于收集对小代码片段的反馈的 Web 应用程序 后端 后端是使用以下库在 haskell 中实现的 REST-ish api: Scotty 用于 REST 接口声明 数据库访问的持久性(使用 sqlite 实现) 用于连载的 Aeson ...

    itsucks-0.4.1开源爬虫

    开源爬虫itsucks,最新版本,可以使用,简单的图形化界面,容易上手

    itsucks开源代码

    爬虫源码,开源 java 很好 强大 可扩展

    itsucks-0.4.1.zip

    itSucks是一个java web spider(web机器人,爬虫)开源项目。支持通过下载模板和正则表达式来定义下载规则。提供一个swing GUI操作界面。

    admiral-sucks:Chrome扩展程序旨在消除Admiral非常邪恶的Adblock Recovery:angry_face_with_horns:

    ,解压缩并将admiral-sucks文件夹拖至chrome://extensions Chrome扩展面板。 为什么? 是一家通过帮助网站将其内容货币化而获利的公司。 它们提供多种服务,但非常令人讨厌的是AdBlock Recovery :首先,它们向网站...

    itsucks:http

    ItSucks 网络爬虫 描述 这个项目是一个具有下载(和恢复)文件能力的java网络蜘蛛(网络爬虫)。 它还可以使用正则表达式和下载模板进行高度定制。 所有后端功能也可在单独的库中使用。 官网 执照 本地开发使用 将 ...

    your-band-sucks-v2:通过不良专辑封面分享音乐

    你的乐队吸v2 通过不良专辑封面共享音乐

    ak2新版内核AKAIO1.5

    + New Super Mario Brothers Minigames on AK2: If they don't work, set Download Play to "Disabled" and boot in non-DMA mode (hold down A while loading) + Misc bug fixes (Too many to list). AK-AIO 1.2 +...

    learnjava:学习AP CS-A考试

    AP的CS A考试其实比较简单,5分还是很容易拿到的,如果你已经有OOP基础只需要学习简单的java语法即可.这个repo的代码基本涵盖了AP考试会涉及的所有用法(对的就是这么点),其中[]?[]:[]三元运算符和Iterator并不会...

    why-your-test-suite-sucks

    这是关于测试的话题 在卡座中启动presentation.markdown以查看

    vld-2.5.1_2.zip

    ", or you think it stinks and would like to say "This thing sucks!", please feel free to [drop us a note][1]. Or, if you'd prefer, you can [contribute a small donation][2]. Both are very appreciated. ...

    weather-sucks:爱沙尼亚语心情天气应用程序

    利用脚本类型module 使用TypeScript编译器通过JSDoc键入check .js 我不建议将这种方法用于生产。 请直接使用TypeScript(如果需要输入安全性)。 即使在如此小的应用程序中,在JSDoc中维护类型也很麻烦。 URL中的...

    sucks:用python制作的小CRUD

    很烂 用python制作的小CRUD

    transcription-sucks:一个帮助转录稍微不那么糟糕的网站

    转录很烂 这是一个简单的项目,我试图让转录变得不那么糟糕。 不要在应用程序之间切换或使用特殊的外围设备,而是在网页上... 后退2秒: ctrl + j 向前跳 2 秒: ctrl + k 加速播放: ctrl + l 添加时间戳: enter

    Professional ScrumMaster's Handbook

    That sucks, and makes me sad—and spurs me to action. I don't want the original vision for ScrumMaster to become lost in the methodology/certifcation wagon train; I want people to reach their full ...

    IE Sucks-crx插件

    IE SUCKS这么糟糕,实际上是有趣的观看失败! IE样式信息条在页面中的障碍码时发光。 无广告! Internet Explorer是一个浏览器的F ****笑话,并字面上持有进步! 在逐步淘汰之前庆祝最终几天,用IE吸收插件。 每当...

    很烂:Ecovacs系列机器人吸尘器的简单命令行脚本

    目前已知可与来自北美和欧洲的Ecovacs Deebot N79,M80 Pro,M81,M88 Pro和R95 MKII一起使用。 它也适用于您的模型吗? 加入“ 的讨论。 如果您对协议感到好奇,那么我会提供。 我会很乐意接受请求。 为什么是...

Global site tag (gtag.js) - Google Analytics