导航:首页 > 源码编译 > javaunsafe源码

javaunsafe源码

发布时间:2022-04-26 21:06:48

1. C#和java有什么不同

C#(C-Sharp)是Microsoft的新编程语言,被誉为“C/C++家族中第一种面向组件的语言”。然而,不管它自己宣称的是什么,许多人认为C#更像是Java的一种克隆,或者是Microsoft用来替代Java的产品。事实是否是这样的呢?

本文的比较结果表明,C#不止是Java的同胞那么简单。如果你是一个Java开发者,想要学习C#或者了解更多有关C#的知识,那么本文就是你必须把最初10分钟投入于其中的所在。

一、C#、C++和Java
C#的语言规范由Microsoft的Anders Hejlsberg与Scott Wiltamuth编写。在当前Microsoft天花乱坠的宣传中,对C#和C++、Java作一番比较总是很有趣的。考虑到当前IT媒体的舆论倾向,如果你早就知道C#更接近Java而不是C++,事情也不值得大惊小怪。对于刚刚加入这场讨论的读者,下面的表1让你自己作出判断。显然,结论应该是:Java和C#虽然不是孪生子,但C#最主要的特色却更接近Java而不是C++。

表1:比较C#、C++和Java最重要的功能
功能 C# C++ Java
继承 允许继承单个类,允许实现多个接口 允许从多个类继承 允许继承单个类,允许实现多个接口
接口实现 通过“interface”关键词 通过抽象类 通过“interface”关键词
内存管理 由运行时环境管理,使用垃圾收集器 需要手工管理 由运行时环境管理,使用垃圾收集器
指针 支持,但只在很少使用的非安全模式下才支持。通常以引用取代指针 支持,一种很常用的功能。 完全不支持。代之以引用。
源代码编译后的形式 .NET中间语言(IL) 可执行代码 字节码
单一的公共基类 是 否 是
异常处理 异常处理 返回错误 异常处理。

了解表1总结的重要语言功能之后,请继续往下阅读,了解C#和Java的一些重要区别。

二、语言规范的比较
2.1、简单数据类型
简单数据类型(Primitive)在C#中称为值类型,C#预定义的简单数据类型比Java多。例如,C#有unit,即无符号整数。表2列出了所有C#的预定义数据类型:

表2:C#中的值类型
类型 说明
object 所有类型的最终极的基类
string 字符串类型;字符串是一个Unicode字符的序列
sbyte 8位带符号整数
short 16位带符号整数
int 32位带符号整数
long 64位带符号整数
byte 8位无符号整数
ushort 16位无符号整数
uint 32位无符号整数
ulong 64位无符号整数
float 单精度浮点数类型
double 双精度浮点数类型
bool 布尔类型;bool值或者是true,或者是false
char 字符类型;一个char值即是一个Unicode字符
decimal 有28位有效数字的高精度小数类型

2.2、常量
忘掉Java中的static final修饰符。在C#中,常量可以用const关键词声明。

public const int x = 55;

此外,C#的设计者还增加了readonly关键词。如果编译器编译时未能确定常量值,你可以使用readonly关键词。readonly域只能通过初始化器或类的构造函数设置。

2.3、公用类的入口点
在Java中,公用类的入口点是一个名为main的公用静态方法。main方法的参数是String对象数组,它没有返回值。在C#中,main方法变成了公用静态方法Main(大写的M),Main方法的参数也是一个String对象数组,而且也没有返回值,如下面的原型声明所示:

public static void Main(String[] args)

但是,C#的Main方法不局限于此。如果不向Main方法传递任何参数,你可以使用上述Main方法的一个重载版本,即不带参数列表的版本。也就是说,下面的Main方法也是一个合法的入口点:

public static void Main()

另外,如果你认为有必要的话,Main方法还可以返回一个int。例如,下面代码中的Main方法返回1:

using System;
public class Hello {
public static int Main() {
Console.WriteLine("Done");
return 1;
}
}

与此相对,在Java中重载main方法是不合法的。

2.4、switch语句
在Java中,switch语句只能处理整数。但C#中的switch语句不同,它还能够处理字符变量。请考虑下面用switch语句处理字符串变量的C#代码:

using System;
public class Hello {
public static void Main(String[] args) {
switch (args[0]) {
case "老板":
Console.WriteLine("早上好!我们随时准备为您效劳!");
break;
case "雇员":
Console.WriteLine("早上好!你可以开始工作了!");
break;
default:
Console.WriteLine("早上好!祝你好运!");
break;
}
}
}

与Java中的switch不同,C#的switch语句要求每一个case块或者在块的末尾提供一个break语句,或者用goto转到switch内的其他case标签。

2.5、foreach语句
foreach语句枚举集合中的各个元素,为集合中的每一个元素执行一次代码块。请参见下面的例子。

using System;
public class Hello {
public static void Main(String[] args) {
foreach (String arg in args)
Console.WriteLine(arg);
}
}

如果在运行这个执行文件的时候指定了参数,比如“Hello Peter Kevin Richard”,则程序的输出将是下面几行文字:

Peter
Kevin
Richard

2.6、C#没有>>>移位操作符
C#支持uint和ulong之类的无符号变量类型。因此,在C#中,右移操作符(即“>>”)对于无符号变量类型和带符号变量类型(比如int和long)的处理方式不同。右移uint和ulong丢弃低位并把空出的高位设置为零;但对于int和long类型的变量,“>>”操作符丢弃低位,同时,只有当变量值是正数时,“>>”才把空出的高位设置成零;如果“>>”操作的是一个负数,空出的高位被设置成为1。

Java中不存在无符号的变量类型。因此,我们用“>>>”操作符在右移时引入负号位;否则,使用“>>”操作符。

2.7、goto关键词
Java不用goto关键词。在C#中,goto允许你转到指定的标签。不过,C#以特别谨慎的态度对待goto,比如它不允许goto转入到语句块的内部。在Java中,你可以用带标签的语句加上break或continue取代C#中的goto。

2.8、声明数组
在Java中,数组的声明方法非常灵活,实际上有许多种声明方法都属于合法的方法。例如,下面的几行代码是等价的:

int[] x = { 0, 1, 2, 3 };
int x[] = { 0, 1, 2, 3 };

但在C#中,只有第一行代码合法,[]不能放到变量名字之后。

2.9、包
在C#中,包(Package)被称为名称空间。把名称空间引入C#程序的关键词是“using”。例如,“using System;”这个语句引入了System名称空间。

然而,与Java不同的是,C#允许为名称空间或者名称空间中的类指定别名:

using TheConsole = System.Console;
public class Hello {
public static void Main() {
TheConsole.WriteLine("使用别名");
}
}

虽然从概念上看,Java的包类似于.NET的名称空间。然而,两者的实现方式不同。在Java中,包的名字同时也是实际存在的实体,它决定了放置.java文件的目录结构。在C#校�锢淼陌�吐呒�拿�浦�涫峭耆�掷氲模�簿褪撬担��瓶占涞拿�植换岫晕锢淼拇虬�绞讲��魏斡跋臁T贑#中,每一个源代码文件可以从属于多个名称空间,而且它可以容纳多个公共类。

.NET中包的实体称为程序集(Assembly)。每一个程序集包含一个manifest结构。manifest列举程序集所包含的文件,控制哪些类型和资源被显露到程序集之外,并把对这些类型和资源的引用映射到包含这些类型与资源的文件。程序集是自包含的,一个程序集可以放置到单一的文件之内,也可以分割成多个文件。.NET的这种封装机制解决了DLL文件所面临的问题,即臭名昭着的DLL Hell问题。

2.10、默认包
在Java中,java.lang包是默认的包,它无需显式导入就已经自动包含。例如,要把一些文本输出到控制台,你可以使用下面的代码:

System.out.println("Hello world from Java");

C#中不存在默认的包。如果要向控制台输出文本,你使用System名称空间Console对象的WriteLine方法。但是,你必须显式导入所有的类。代码如下:

using System;
public class Hello {
public static void Main() {
Console.WriteLine("Hello world from C#");
}
}

2.11、面向对象
Java和C#都是完全面向对象的语言。在面向对象编程的三大原则方面,这两种语言接近得不能再接近。

继承:这两种语言都支持类的单一继承,但类可以实现多个接口。所有类都从一个公共的基类继承。
封装与可见性:无论是在Java还是C#中,你都可以决定类成员是否可见。除了C#的internal访问修饰符之外,两者的可见性机制非常相似。
多态性:Java和C#都支持某些形式的多态性机制,且两者实现方法非常类似。
2.12、可访问性
类的每个成员都有特定类型的可访问性。C#中的访问修饰符与Java中的基本对应,但多出了一个internal。简而言之,C#有5种类型的可访问性,如下所示:

public:成员可以从任何代码访问。
protected:成员只能从派生类访问。
internal:成员只能从同一程序集的内部访问。
protected internal:成员只能从同一程序集内的派生类访问。
private:成员只能在当前类的内部访问。
2.13、派生类
在Java中,我们用关键词“extends”实现继承。C#采用了C++的类派生语法。例如,下面的代码显示了如何派生父类Control从而创建出新类Button:

public class Button: Control { . . }

2.14、最终类
由于C#中不存在final关键词,如果想要某个类不再被派生,你可以使用sealed关键词,如下例所示:

sealed class FinalClass { . . }

2.15、接口
接口这个概念在C#和Java中非常相似。接口的关键词是interface,一个接口可以扩展一个或者多个其他接口。按照惯例,接口的名字以大写字母“I”开头。下面的代码是C#接口的一个例子,它与Java中的接口完全一样:

interface IShape { void Draw(); }

扩展接口的语法与扩展类的语法一样。例如,下例的IRectangularShape接口扩展IShape接口(即,从IShape接口派生出IRectangularShape接口)。

interface IRectangularShape: IShape { int GetWidth(); }

如果你从两个或者两个以上的接口派生,父接口的名字列表用逗号分隔,如下面的代码所示:

interface INewInterface: IParent1, IParent2 { }

然而,与Java不同,C#中的接口不能包含域(Field)。

另外还要注意,在C#中,接口内的所有方法默认都是公用方法。在Java中,方法声明可以带有public修饰符(即使这并非必要),但在C#中,显式为接口的方法指定public修饰符是非法的。例如,下面的C#接口将产生一个编译错误。

interface IShape { public void Draw(); }

2.16、is和as操作符
C#中的is操作符与Java中的instanceof操作符一样,两者都可以用来测试某个对象的实例是否属于特定的类型。在Java中没有与C#中的as操作符等价的操作符。as操作符与is操作符非常相似,但它更富有“进取心”:如果类型正确的话,as操作符会尝试把被测试的对象引用转换成目标类型;否则,它把变量引用设置成null。

为正确理解as操作符,首先请考虑下面这个例子中is操作符的运用。这个例子包含一个IShape接口,以及两个实现了IShape接口的类Rectangle和Circle。

using System;
interface IShape {
void draw();
}
public class Rectangle: IShape {
public void draw() {
}
public int GetWidth() {
return 6;
}
}
public class Circle: IShape {
public void draw() {
}
public int GetRadius() {
return 5;
}
}
public class LetsDraw {
public static void Main(String[] args) {
IShape shape = null;
if (args[0] == "rectangle") {
shape = new Rectangle();
}
else if (args[0] == "circle") {
shape = new Circle();
}
if (shape is Rectangle) {
Rectangle rectangle = (Rectangle) shape;
Console.WriteLine("Width : " + rectangle.GetWidth());
}
if (shape is Circle) {
Circle circle = (Circle) shape;
Console.WriteLine("Radius : " + circle.GetRadius());
}
}
}

编译好代码之后,用户可以输入“rectangle”或者“circle”作为Main方法的参数。如果用户输入的是“circle”,则shape被实例化成为一个Circle类型的对象;反之,如果用户输入的是“rectangle”,则shape被实例化成为Rectangle类型的对象。随后,程序用is操作符测试shape的变量类型:如果shape是一个矩形,则shape被转换成为Rectangle对象,我们调用它的GetWidth方法;如果shape是一个圆,则shape被转换成为一个Circle对象,我们调用它的GetRadius方法。

如果使用as操作符,则上述代码可以改成如下形式:

using System;
interface IShape {
void draw();
}
public class Rectangle: IShape {
public void draw() {
}
public int GetWidth() {
return 6;
}
}
public class Circle: IShape {
public void draw() {
}
public int GetRadius() {
return 5;
}
}
public class LetsDraw {
public static void Main(String[] args) {
IShape shape = null;
if (args[0] == "rectangle") {
shape = new Rectangle();
}
else if (args[0] == "circle") {
shape = new Circle();
}
Rectangle rectangle = shape as Rectangle;
if (rectangle != null) {
Console.WriteLine("Width : " + rectangle.GetWidth());
}
else {
Circle circle = shape as Circle;
if (circle != null)
Console.WriteLine("Radius : " + circle.GetRadius());
}
}
}

在上面代码的粗体部分中,我们在没有测试shape对象类型的情况下,就用as操作符把shape转换成Rectangle类型的对象。如果shape正好是一个Rectangle,则shape被转换成为Rectangle类型的对象并保存到rectangle变量,然后我们调用它的GetWidth方法。如果这种转换失败,则我们进行第二次尝试。这一次,shape被转换成为Circle类型的对象并保存到circle变量。如果shape确实是一个Circle对象,则circle现在引用了一个Circle对象,我们调用它的GetRadius方法。

2.17、库
C#没有自己的类库。但是,C#共享了.NET的类库。当然,.NET类库也可以用于其他.NET语言,比如VB.NET或者JScript.NET。值得一提的是StringBuilder类,它是对String类的补充。StringBuilder类与Java的StringBuffer类非常相似。

2.18、垃圾收集
C++已经让我们认识到手工管理内存是多么缺乏效率和浪费时间。当你在C++中创建了一个对象,你就必须手工地拆除这个对象。代码越复杂,这个任务也越困难。Java用垃圾收集器来解决这个问题,由垃圾收集器搜集不再使用的对象并释放内存。C#同样采用了这种方法。应该说,如果你也在开发一种新的OOP语言,追随这条道路是一种非常自然的选择。C#仍旧保留了C++的内存手工管理方法,它适合在速度极端重要的场合使用,而在Java中这是不允许的。

2.19、异常处理
如果你听说C#使用与Java相似的异常处理机制,你不会为此而惊讶,对吧?在C#中,所有的异常都从一个名为Exception的类派生(听起来很熟悉?)另外,正如在Java中一样,你还有熟悉的try和catch语句。Exception类属于.NET System名称空间的一部分。

三、Java没有的功能
C#出生在Java成熟之后,因此,C#拥有一些Java(目前)还没有的绝妙功能也就不足为奇。

3.1、枚举器
枚举器即enum类型(Enumerator,或称为计数器),它是一个相关常量的集合。精确地说,enum类型声明为一组相关的符号常量定义了一个类型名字。例如,你可以创建一个名为Fruit(水果)的枚举器,把它作为一个变量值的类型使用,从而把变量可能的取值范围限制为枚举器中出现的值。

public class Demo {
public enum Fruit {
Apple, Banana, Cherry, Durian
}
public void Process(Fruit fruit) {
switch (fruit) {
case Fruit.Apple:
...
break;
case Fruit.Banana:
...
break;
case Fruit.Cherry:
...
break;
case Fruit.Durian:
...
break;
}
}
}

在上例的Process方法中,虽然你可以用int作为myVar变量的类型,但是,使用枚举器Fruit之后,变量的取值范围限制到了Applet、Banana、Cherry和Durian这几个值之内。与int相比,enum的可读性更好,自我说明能力更强。

3.2、结构
结构(Struct)与类很相似。然而,类是作为一种引用类型在堆中创建,而结构是一种值类型,它存储在栈中或者是嵌入式的。因此,只要谨慎运用,结构要比类快。结构可以实现接口,可以象类一样拥有成员,但结构不支持继承。

然而,简单地用结构来取代类可能导致惨重损失。这是因为,结构是以值的方式传递,由于这种传递方式要把值复制到新的位置,所以传递一个“肥胖的”结构需要较大的开销。而对于类,传递的时候只需传递它的引用。

下面是一个结构的例子。注意它与类非常相似,只要把单词“struct”替换成“class”,你就得到了一个类。

struct Point {
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}

3.3、属性
C#类除了可以拥有域(Field)之外,它还可以拥有属性(Property)。属性是一个与类或对象关联的命名的特征。属性是域的一种自然扩展――两者都是有类型、有名字的类成员。然而,和域不同的是,属性不表示存储位置;相反,属性拥有存取器(accessor),存取器定义了读取或者写入属性值时必须执行的代码。因此,属性提供了一种把动作和读取、写入对象属性值的操作关联起来的机制,而且它们允许属性值通过计算得到。

在C#中,属性通过属性声明语法定义。属性声明语法的第一部分与域声明很相似,第二部分包括一个set过程和/或一个get过程。例如,在下面的例子中,PropertyDemo类定义了一个Prop属性。

public class PropertyDemo {
private string prop;
public string Prop {
get {
return prop;
}
set {
prop = value;
}
}
}

如果属性既允许读取也允许写入,如PropertyDemo类的Prop属性,则它同时拥有get和set存取过程。当我们读取属性的值时,get存取过程被调用;当我们写入属性值时,set存取过程被调用。在set存取过程中,属性的新值在一个隐含的value参数中给出。

与读取和写入域的方法一样,属性也可以用同样的语法读取和写入。例如,下面的代码实例化了一个PropertyDemo类,然后写入、读取它的Prop属性。

PropertyDemo pd = new PropertyDemo();
pd.Prop = "123"; // set
string s = pd.Prop; // get

3.4、以引用方式传递简单数据类型的参数
在Java中,当你把一个简单数据类型的值作为参数传递给方法时,参数总是以值的方式传递――即,系统将为被调用的方法创建一个参数值的副本。在C#中,你可以用引用的方式传递一个简单数据类型的值。此时,被调用的方法将直接使用传递给它的那个值――也就是说,如果在被调用方法内部修改了参数的值,则原来的变量值也随之改变。

在C#中以引用方式传递值时,我们使用ref关键词。例如,如果编译并运行下面的代码,你将在控制台上看到输出结果16。注意i值被传递给ProcessNumber之后是如何被改变的。

using System;
public class PassByReference {
public static void Main(String[] args) {
int i = 8;
ProcessNumber(ref i);
Console.WriteLine(i);
}
public static void ProcessNumber(ref int j) {
j = 16;
}
}

C#中还有一个允许以引用方式传递参数的关键词out,它与ref相似。但是,使用out时,作为参数传递的变量在传递之前不必具有已知的值。在上例中,如果整数i在传递给ProcessNumber方法之前没有初始化,则代码将出错。如果用out来取代ref,你就可以传递一个未经初始化的值,如下面这个修改后的例子所示。

using System;
public class PassByReference {
public static void Main(String[] args) {
int i;
ProcessNumber(out i);
Console.WriteLine(i);
}
public static void ProcessNumber(out int j) {
j = 16;
}
}

经过修改之后,虽然i值在传递给ProcessNumber方法之前没有初始化,但PassByReference类能够顺利通过编译。

3.5、C#保留了指针
对于那些觉得自己能够恰到好处地运用指针并乐意手工进行内存管理的开发者来说,在C#中,他们仍旧可以用既不安全也不容易使用的“古老的”指针来提高程序的性能。C#提供了支持“不安全”(unsafe)代码的能力,这种代码能够直接操作指针,能够“固定”对象以便临时地阻止垃圾收集器移动对象。无论从开发者还是用户的眼光来看,这种对“不安全”代码的支持其实是一种安全功能。“不安全”的代码必须用unsafe关键词显式地标明,因此开发者不可能在无意之中使用“不安全”的代码。同时,C#编译器又和执行引擎协作,保证了“不安全”的代码不能伪装成为安全代码。

using System;
class UsePointer {
unsafe static void PointerDemo(byte[] arr) {
.
.
}
}

C#中的unsafe代码适合在下列情形下使用:当速度极端重要时,或者当对象需要与现有的软件(比如COM对象或者DLL形式的C代码)交互时。

3.6、代理
代理(delegate)可以看作C++或者其他语言中的函数指针。然而,与函数指针不同的是,C#中的代理是面向对象的、类型安全的、可靠的。而且,函数指针只能用来引用静态函数,但代理既能够引用静态方法,也能够引用实例方法。代理用来封装可调用方法。你可以在类里面编写方法并在该方法上创建代理,此后这个代理就可以被传递到第二个方法。这样,第二个方法就可以调用第一个方法。

代理是从公共基类System.Delegate派生的引用类型。定义和使用代理包括三个步骤:声明,创建实例,调用。代理用delegate声明语法声明。例如,一个不需要参数且没有返回值的代理可以用如下代码声明:

delegate void TheDelegate();

创建代理实例的语法是:使用new关键词,并引用一个实例或类方法,该方法必须符合代理指定的特征。一旦创建了代理的实例,我们就可以用调用方法的语法调用它。

3.7、包装和解除包装
在面向对象的编程语言中,我们通常使用的是对象。但为了提高速度,C#也提供了简单数据类型。因此,C#程序既包含一大堆的对象,又有大量的值。在这种环境下,让这两者协同工作始终是一个不可回避的问题,你必须要有一种让引用和值进行通信的方法。

在C#以及.NET运行时环境中,这个“通信”问题通过包装(Boxing)和解除包装(Unboxing)解决。包装是一种让值类型看起来象引用类型的处理过程。当一个值类型(简单数据类型)被用于一个要求或者可以使用对象的场合时,包装操作自动进行。包装一个value-type值的步骤包括:分配一个对象实例,然后把value-type值复制到对象实例。

解除包装所执行的动作与包装相反,它把一个引用类型转换成值类型。解除包装操作的步骤包括:首先检查并确认对象实例确实是给定value-type的一个经过包装的值,然后从对象实例复制出值。

Java对该问题的处理方式略有不同。Java为每一种简单数据类型提供了一个对应的类封装器。例如,用Integer类封装int类型,用Byte类封装byte类型。

【结束语】本文为你比较了C#和Java。这两种语言很相似,然而,说C#是Java的克隆或许已经大大地言过其实。面向对象、中间语言这类概念并不是什么新东西。如果你准备设计一种面向对象的新语言,而且它必须在一个受管理的安全环境内运行,你难道不会搞出与C#差不多的东西吗?

2. 现在学C# 好,还是Java比较好

华山论剑:C#对Java
(仙人掌工作室 2001年06月29日 17:01)

C#(C-Sharp)是Microsoft的新编程语言,被誉为“C/C++家族中第一种面向组件的语言”。然而,不管它自己宣称的是什么,许多人认为C#更像是Java的一种克隆,或者是Microsoft用来替代Java的产品。事实是否是这样的呢?

本文的比较结果表明,C#不止是Java的同胞那么简单。如果你是一个Java开发者,想要学习C#或者了解更多有关C#的知识,那么本文就是你必须把最初10分钟投入于其中的所在。

一、C#、C++和Java
C#的语言规范由Microsoft的Anders Hejlsberg与Scott Wiltamuth编写。在当前Microsoft天花乱坠的宣传中,对C#和C++、Java作一番比较总是很有趣的。考虑到当前IT媒体的舆论倾向,如果你早就知道C#更接近Java而不是C++,事情也不值得大惊小怪。对于刚刚加入这场讨论的读者,下面的表1让你自己作出判断。显然,结论应该是:Java和C#虽然不是孪生子,但C#最主要的特色却更接近Java而不是C++。

表1:比较C#、C++和Java最重要的功能
功能 C# C++ Java
继承 允许继承单个类,允许实现多个接口 允许从多个类继承 允许继承单个类,允许实现多个接口
接口实现 通过“interface”关键词 通过抽象类 通过“interface”关键词
内存管理 由运行时环境管理,使用垃圾收集器 需要手工管理 由运行时环境管理,使用垃圾收集器
指针 支持,但只在很少使用的非安全模式下才支持。通常以引用取代指针 支持,一种很常用的功能。 完全不支持。代之以引用。
源代码编译后的形式 .NET中间语言(IL) 可执行代码 字节码
单一的公共基类 是 否 是
异常处理 异常处理 返回错误 异常处理。

了解表1总结的重要语言功能之后,请继续往下阅读,了解C#和Java的一些重要区别。

二、语言规范的比较
2.1、简单数据类型
简单数据类型(Primitive)在C#中称为值类型,C#预定义的简单数据类型比Java多。例如,C#有unit,即无符号整数。表2列出了所有C#的预定义数据类型:

表2:C#中的值类型
类型 说明
object 所有类型的最终极的基类
string 字符串类型;字符串是一个Unicode字符的序列
sbyte 8位带符号整数
short 16位带符号整数
int 32位带符号整数
long 64位带符号整数
byte 8位无符号整数
ushort 16位无符号整数
uint 32位无符号整数
ulong 64位无符号整数
float 单精度浮点数类型
double 双精度浮点数类型
bool 布尔类型;bool值或者是true,或者是false
char 字符类型;一个char值即是一个Unicode字符
decimal 有28位有效数字的高精度小数类型

2.2、常量
忘掉Java中的static final修饰符。在C#中,常量可以用const关键词声明。

public const int x = 55;

此外,C#的设计者还增加了readonly关键词。如果编译器编译时未能确定常量值,你可以使用readonly关键词。readonly域只能通过初始化器或类的构造函数设置。

2.3、公用类的入口点
在Java中,公用类的入口点是一个名为main的公用静态方法。main方法的参数是String对象数组,它没有返回值。在C#中,main方法变成了公用静态方法Main(大写的M),Main方法的参数也是一个String对象数组,而且也没有返回值,如下面的原型声明所示:

public static void Main(String[] args)

但是,C#的Main方法不局限于此。如果不向Main方法传递任何参数,你可以使用上述Main方法的一个重载版本,即不带参数列表的版本。也就是说,下面的Main方法也是一个合法的入口点:

public static void Main()

另外,如果你认为有必要的话,Main方法还可以返回一个int。例如,下面代码中的Main方法返回1:

using System;
public class Hello {
public static int Main() {
Console.WriteLine("Done");
return 1;
}
}

与此相对,在Java中重载main方法是不合法的。

2.4、switch语句
在Java中,switch语句只能处理整数。但C#中的switch语句不同,它还能够处理字符变量。请考虑下面用switch语句处理字符串变量的C#代码:

using System;
public class Hello {
public static void Main(String[] args) {
switch (args[0]) {
case "老板":
Console.WriteLine("早上好!我们随时准备为您效劳!");
break;
case "雇员":
Console.WriteLine("早上好!你可以开始工作了!");
break;
default:
Console.WriteLine("早上好!祝你好运!");
break;
}
}
}

与Java中的switch不同,C#的switch语句要求每一个case块或者在块的末尾提供一个break语句,或者用goto转到switch内的其他case标签。

2.5、foreach语句
foreach语句枚举集合中的各个元素,为集合中的每一个元素执行一次代码块。请参见下面的例子。

using System;
public class Hello {
public static void Main(String[] args) {
foreach (String arg in args)
Console.WriteLine(arg);
}
}

如果在运行这个执行文件的时候指定了参数,比如“Hello Peter Kevin Richard”,则程序的输出将是下面几行文字:

Peter
Kevin
Richard

2.6、C#没有>>>移位操作符
C#支持uint和ulong之类的无符号变量类型。因此,在C#中,右移操作符(即“>>”)对于无符号变量类型和带符号变量类型(比如int和long)的处理方式不同。右移uint和ulong丢弃低位并把空出的高位设置为零;但对于int和long类型的变量,“>>”操作符丢弃低位,同时,只有当变量值是正数时,“>>”才把空出的高位设置成零;如果“>>”操作的是一个负数,空出的高位被设置成为1。

Java中不存在无符号的变量类型。因此,我们用“>>>”操作符在右移时引入负号位;否则,使用“>>”操作符。

2.7、goto关键词
Java不用goto关键词。在C#中,goto允许你转到指定的标签。不过,C#以特别谨慎的态度对待goto,比如它不允许goto转入到语句块的内部。在Java中,你可以用带标签的语句加上break或continue取代C#中的goto。

2.8、声明数组
在Java中,数组的声明方法非常灵活,实际上有许多种声明方法都属于合法的方法。例如,下面的几行代码是等价的:

int[] x = { 0, 1, 2, 3 };
int x[] = { 0, 1, 2, 3 };

但在C#中,只有第一行代码合法,[]不能放到变量名字之后。

2.9、包
在C#中,包(Package)被称为名称空间。把名称空间引入C#程序的关键词是“using”。例如,“using System;”这个语句引入了System名称空间。

然而,与Java不同的是,C#允许为名称空间或者名称空间中的类指定别名:

using TheConsole = System.Console;
public class Hello {
public static void Main() {
TheConsole.WriteLine("使用别名");
}
}

虽然从概念上看,Java的包类似于.NET的名称空间。然而,两者的实现方式不同。在Java中,包的名字同时也是实际存在的实体,它决定了放置.java文件的目录结构。在C#中,物理的包和逻辑的名称之间是完全分离的,也就是说,名称空间的名字不会对物理的打包方式产生任何影响。在C#中,每一个源代码文件可以从属于多个名称空间,而且它可以容纳多个公共类。

.NET中包的实体称为程序集(Assembly)。每一个程序集包含一个manifest结构。manifest列举程序集所包含的文件,控制哪些类型和资源被显露到程序集之外,并把对这些类型和资源的引用映射到包含这些类型与资源的文件。程序集是自包含的,一个程序集可以放置到单一的文件之内,也可以分割成多个文件。.NET的这种封装机制解决了DLL文件所面临的问题,即臭名昭着的DLL Hell问题。

2.10、默认包
在Java中,java.lang包是默认的包,它无需显式导入就已经自动包含。例如,要把一些文本输出到控制台,你可以使用下面的代码:

System.out.println("Hello world from Java");

C#中不存在默认的包。如果要向控制台输出文本,你使用System名称空间Console对象的WriteLine方法。但是,你必须显式导入所有的类。代码如下:

using System;
public class Hello {
public static void Main() {
Console.WriteLine("Hello world from C#");
}
}

2.11、面向对象
Java和C#都是完全面向对象的语言。在面向对象编程的三大原则方面,这两种语言接近得不能再接近。

继承:这两种语言都支持类的单一继承,但类可以实现多个接口。所有类都从一个公共的基类继承。
封装与可见性:无论是在Java还是C#中,你都可以决定类成员是否可见。除了C#的internal访问修饰符之外,两者的可见性机制非常相似。
多态性:Java和C#都支持某些形式的多态性机制,且两者实现方法非常类似。
2.12、可访问性
类的每个成员都有特定类型的可访问性。C#中的访问修饰符与Java中的基本对应,但多出了一个internal。简而言之,C#有5种类型的可访问性,如下所示:

public:成员可以从任何代码访问。
protected:成员只能从派生类访问。
internal:成员只能从同一程序集的内部访问。
protected internal:成员只能从同一程序集内的派生类访问。
private:成员只能在当前类的内部访问。
2.13、派生类
在Java中,我们用关键词“extends”实现继承。C#采用了C++的类派生语法。例如,下面的代码显示了如何派生父类Control从而创建出新类Button:

public class Button: Control { . . }

2.14、最终类
由于C#中不存在final关键词,如果想要某个类不再被派生,你可以使用sealed关键词,如下例所示:

sealed class FinalClass { . . }

2.15、接口
接口这个概念在C#和Java中非常相似。接口的关键词是interface,一个接口可以扩展一个或者多个其他接口。按照惯例,接口的名字以大写字母“I”开头。下面的代码是C#接口的一个例子,它与Java中的接口完全一样:

interface IShape { void Draw(); }

扩展接口的语法与扩展类的语法一样。例如,下例的IRectangularShape接口扩展IShape接口(即,从IShape接口派生出IRectangularShape接口)。

interface IRectangularShape: IShape { int GetWidth(); }

如果你从两个或者两个以上的接口派生,父接口的名字列表用逗号分隔,如下面的代码所示:

interface INewInterface: IParent1, IParent2 { }

然而,与Java不同,C#中的接口不能包含域(Field)。

另外还要注意,在C#中,接口内的所有方法默认都是公用方法。在Java中,方法声明可以带有public修饰符(即使这并非必要),但在C#中,显式为接口的方法指定public修饰符是非法的。例如,下面的C#接口将产生一个编译错误。

interface IShape { public void Draw(); }

2.16、is和as操作符
C#中的is操作符与Java中的instanceof操作符一样,两者都可以用来测试某个对象的实例是否属于特定的类型。在Java中没有与C#中的as操作符等价的操作符。as操作符与is操作符非常相似,但它更富有“进取心”:如果类型正确的话,as操作符会尝试把被测试的对象引用转换成目标类型;否则,它把变量引用设置成null。

为正确理解as操作符,首先请考虑下面这个例子中is操作符的运用。这个例子包含一个IShape接口,以及两个实现了IShape接口的类Rectangle和Circle。

using System;
interface IShape {
void draw();
}
public class Rectangle: IShape {
public void draw() {
}
public int GetWidth() {
return 6;
}
}
public class Circle: IShape {
public void draw() {
}
public int GetRadius() {
return 5;
}
}
public class LetsDraw {
public static void Main(String[] args) {
IShape shape = null;
if (args[0] == "rectangle") {
shape = new Rectangle();
}
else if (args[0] == "circle") {
shape = new Circle();
}
if (shape is Rectangle) {
Rectangle rectangle = (Rectangle) shape;
Console.WriteLine("Width : " + rectangle.GetWidth());
}
if (shape is Circle) {
Circle circle = (Circle) shape;
Console.WriteLine("Radius : " + circle.GetRadius());
}
}
}

编译好代码之后,用户可以输入“rectangle”或者“circle”作为Main方法的参数。如果用户输入的是“circle”,则shape被实例化成为一个Circle类型的对象;反之,如果用户输入的是“rectangle”,则shape被实例化成为Rectangle类型的对象。随后,程序用is操作符测试shape的变量类型:如果shape是一个矩形,则shape被转换成为Rectangle对象,我们调用它的GetWidth方法;如果shape是一个圆,则shape被转换成为一个Circle对象,我们调用它的GetRadius方法。

如果使用as操作符,则上述代码可以改成如下形式:

using System;
interface IShape {
void draw();
}
public class Rectangle: IShape {
public void draw() {
}
public int GetWidth() {
return 6;
}
}
public class Circle: IShape {
public void draw() {
}
public int GetRadius() {
return 5;
}
}
public class LetsDraw {
public static void Main(String[] args) {
IShape shape = null;
if (args[0] == "rectangle") {
shape = new Rectangle();
}
else if (args[0] == "circle") {
shape = new Circle();
}
Rectangle rectangle = shape as Rectangle;
if (rectangle != null) {
Console.WriteLine("Width : " + rectangle.GetWidth());
}
else {
Circle circle = shape as Circle;
if (circle != null)
Console.WriteLine("Radius : " + circle.GetRadius());
}
}
}

在上面代码的粗体部分中,我们在没有测试shape对象类型的情况下,就用as操作符把shape转换成Rectangle类型的对象。如果shape正好是一个Rectangle,则shape被转换成为Rectangle类型的对象并保存到rectangle变量,然后我们调用它的GetWidth方法。如果这种转换失败,则我们进行第二次尝试。这一次,shape被转换成为Circle类型的对象并保存到circle变量。如果shape确实是一个Circle对象,则circle现在引用了一个Circle对象,我们调用它的GetRadius方法。

2.17、库
C#没有自己的类库。但是,C#共享了.NET的类库。当然,.NET类库也可以用于其他.NET语言,比如VB.NET或者JScript.NET。值得一提的是StringBuilder类,它是对String类的补充。StringBuilder类与Java的StringBuffer类非常相似。

2.18、垃圾收集
C++已经让我们认识到手工管理内存是多么缺乏效率和浪费时间。当你在C++中创建了一个对象,你就必须手工地拆除这个对象。代码越复杂,这个任务也越困难。Java用垃圾收集器来解决这个问题,由垃圾收集器搜集不再使用的对象并释放内存。C#同样采用了这种方法。应该说,如果你也在开发一种新的OOP语言,追随这条道路是一种非常自然的选择。C#仍旧保留了C++的内存手工管理方法,它适合在速度极端重要的场合使用,而在Java中这是不允许的。

2.19、异常处理
如果你听说C#使用与Java相似的异常处理机制,你不会为此而惊讶,对吧?在C#中,所有的异常都从一个名为Exception的类派生(听起来很熟悉?)另外,正如在Java中一样,你还有熟悉的try和catch语句。Exception类属于.NET System名称空间的一部分。

三、Java没有的功能
C#出生在Java成熟之后,因此,C#拥有一些Java(目前)还没有的绝妙功能也就不足为奇。

3.1、枚举器
枚举器即enum类型(Enumerator,或称为计数器),它是一个相关常量的集合。精确地说,enum类型声明为一组相关的符号常量定义了一个类型名字。例如,你可以创建一个名为Fruit(水果)的枚举器,把它作为一个变量值的类型使用,从而把变量可能的取值范围限制为枚举器中出现的值。

public class Demo {
public enum Fruit {
Apple, Banana, Cherry, Durian
}
public void Process(Fruit fruit) {
switch (fruit) {
case Fruit.Apple:
...
break;
case Fruit.Banana:
...
break;
case Fruit.Cherry:
...
break;
case Fruit.Durian:
...
break;
}
}
}

在上例的Process方法中,虽然你可以用int作为myVar变量的类型,但是,使用枚举器Fruit之后,变量的取值范围限制到了Applet、Banana、Cherry和Durian这几个值之内。与int相比,enum的可读性更好,自我说明能力更强。

3.2、结构
结构(Struct)与类很相似。然而,类是作为一种引用类型在堆中创建,而结构是一种值类型,它存储在栈中或者是嵌入式的。因此,只要谨慎运用,结构要比类快。结构可以实现接口,可以象类一样拥有成员,但结构不支持继承。

然而,简单地用结构来取代类可能导致惨重损失。这是因为,结构是以值的方式传递,由于这种传递方式要把值复制到新的位置,所以传递一个“肥胖的”结构需要较大的开销。而对于类,传递的时候只需传递它的引用。

下面是一个结构的例子。注意它与类非常相似,只要把单词“struct”替换成“class”,你就得到了一个类。

struct Point {
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}

3.3、属性
C#类除了可以拥有域(Field)之外,它还可以拥有属性(Property)。属性是一个与类或对象关联的命名的特征。属性是域的一种自然扩展――两者都是有类型、有名字的类成员。然而,和域不同的是,属性不表示存储位置;相反,属性拥有存取器(accessor),存取器定义了读取或者写入属性值时必须执行的代码。因此,属性提供了一种把动作和读取、写入对象属性值的操作关联起来的机制,而且它们允许属性值通过计算得到。

在C#中,属性通过属性声明语法定义。属性声明语法的第一部分与域声明很相似,第二部分包括一个set过程和/或一个get过程。例如,在下面的例子中,PropertyDemo类定义了一个Prop属性。

public class PropertyDemo {
private string prop;
public string Prop {
get {
return prop;
}
set {
prop = value;
}
}
}

如果属性既允许读取也允许写入,如PropertyDemo类的Prop属性,则它同时拥有get和set存取过程。当我们读取属性的值时,get存取过程被调用;当我们写入属性值时,set存取过程被调用。在set存取过程中,属性的新值在一个隐含的value参数中给出。

与读取和写入域的方法一样,属性也可以用同样的语法读取和写入。例如,下面的代码实例化了一个PropertyDemo类,然后写入、读取它的Prop属性。

PropertyDemo pd = new PropertyDemo();
pd.Prop = "123"; // set
string s = pd.Prop; // get

3.4、以引用方式传递简单数据类型的参数
在Java中,当你把一个简单数据类型的值作为参数传递给方法时,参数总是以值的方式传递――即,系统将为被调用的方法创建一个参数值的副本。在C#中,你可以用引用的方式传递一个简单数据类型的值。此时,被调用的方法将直接使用传递给它的那个值――也就是说,如果在被调用方法内部修改了参数的值,则原来的变量值也随之改变。

在C#中以引用方式传递值时,我们使用ref关键词。例如,如果编译并运行下面的代码,你将在控制台上看到输出结果16。注意i值被传递给ProcessNumber之后是如何被改变的。

using System;
public class PassByReference {
public static void Main(String[] args) {
int i = 8;
ProcessNumber(ref i);
Console.WriteLine(i);
}
public static void ProcessNumber(ref int j) {
j = 16;
}
}

C#中还有一个允许以引用方式传递参数的关键词out,它与ref相似。但是,使用out时,作为参数传递的变量在传递之前不必具有已知的值。在上例中,如果整数i在传递给ProcessNumber方法之前没有初始化,则代码将出错。如果用out来取代ref,你就可以传递一个未经初始化的值,如下面这个修改后的例子所示。

using System;
public class PassByReference {
public static void Main(String[] args) {
int i;
ProcessNumber(out i);
Console.WriteLine(i);
}
public static void ProcessNumber(out int j) {
j = 16;
}
}

经过修改之后,虽然i值在传递给ProcessNumber方法之前没有初始化,但PassByReference类能够顺利通过编译。

3.5、C#保留了指针
对于那些觉得自己能够恰到好处地运用指针并乐意手工进行内存管理的开发者来说,在C#中,他们仍旧可以用既不安全也不容易使用的“古老的”指针来提高程序的性能。C#提供了支持“不安全”(unsafe)代码的能力,这种代码能够直接操作指针,能够“固定”对象以便临时地阻止垃圾收集器移动对象。无论从开发者还是用户的眼光来看,这种对“不安全”代码的支持其实是一种安全功能。“不安全”的代码必须用unsafe关键词显式地标明,因此开发者不可能在无意之中使用“不安全”的代码。同时,C#编译器又和执行引擎协作,保证了“不安全”的代码不能伪装成为安全代码。

using System;
class UsePointer {
unsafe static void PointerDemo(byte[] arr) {
.
.
}
}

C#中的unsafe代码适合在下列情形下使用:当速度极端重要时,或者当对象需要与现有的软件(比如COM对象或者DLL形式的C代码)交互时。

3.6、代理
代理(delegate)可以看作C++或者其他语言中的函数指针。然而,与函数指针不同的是,C#中的代理是面向对象的、类型安全的、可靠的。而且,函数指针只能用来引用静态函数,但代理既能够引用静态方法,也能够引用实例方法。代理用来封装可调用方法。你可以在类里面编写方法并在该方法上创建代理,此后这个代理就可以被传递到第二个方法。这样,第二个方法就可以调用第一个方法。

代理是从公共基类System.Delegate派生的引用类型。定义和使用代理包括三个步骤:声明,创建实例,调用。代理用delegate声明语法声明。例如,一个不需要参数且没有返回值的代理可以用如下代码声明:

delegate void TheDelegate();

创建代理实例的语法是:使用new关键词,并引用一个实例或类方法,该方法必须符合代理指定的特征。一旦创建了代理的实例,我们就可以用调用方法的语法调用它。

3.7、包装和解除包装
在面向对象的编程语言中,我们通常使用的是对象。但为了提高速度,C#也提供了简单数据类型。因此,C#程序既包含一大堆的对象,又有大量的值。在这种环境下,让这两者协同工作始终是一个不可回避的问题,你必须要有一种让引用和值进行通信的方法。

在C#以及.NET运行时环境中,这个“通信”问题通过包装(Boxing)和解除包装(Unboxing)解决。包装是一种让值类型看起来象引用类型的处理过程。当一个值类型(简单数据类型)被用于一个要求或者可以使用对象的场合时,包装操作自动进行。包装一个value-type值的步骤包括:分配一个对象实例,然后把value-type值复制到对象实例。

解除包装所执行的动作与包装相反,它把一个引用类型转换成值类型。解除包装操作的步骤包括:首先检查并确认对象实例确实是给定value-type的一个经过包装的值,然后从对象实例复制出值。

Java对该问题的处理方式略有不同。Java为每一种简单数据类型提供了一个对应的类封装器。例如,用Integer类封装int类型,用Byte类封装byte类型。

【结束语】本文为你比较了C#和Java。这两种语言很相似,然而,说C#是Java的克隆或许已经大大地言过其实。面向对象、中间语言这类概念并不是什么新东西。如果你准备设计一种面向对象的新语言,而且它必须在一个受管理的安全环境内运行,你难道不会搞出与C#差不多的东西吗?

3. 微信谷歌验证器打不开怎么处理

摘要 如果我们下载的谷歌二次验证器是不能使用的,那么可以直接利用微信小程序来进行操作,这样就可以直接绑定好我们的微信,处理起来也是比较容易的。我们可以先注册好自己的账号,然后再进行验证码方面的确定。不管是成为会员还是直接使用二次验证码都是可以的,但是一般来讲直接成为会员要更好一些,这样至少是后期找回秘钥等也是要更加容易一些。

4. 线程池中空闲的线程处于什么状态

一:阻塞状态,线程并没有销毁,也没有得到CPU时间片执行;

源码追踪:
for (;;) {
...
workQueue.take();
...
}
public E take()...{
...
while (count.get() == 0) { / /这里就是任务队列中的消息数量
notEmpty.await();
}
...
}

public final void await()...{
...
LockSupport.park(this);

...
}

继续往下:
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
U.park(false, 0L);
setBlocker(t, null);
}

private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();

//线程调用该方法,线程将一直阻塞直到超时,或者是中断条件出现。

public native void park(boolean isAbsolute, long time);
上面就是java11线程池中阻塞的源码追踪;

二.对比object的wait()方法:
@FastNative
public final native void wait(long timeout, int nanos) throws InterruptedException;
还有Thread的sleep() 方法:
@FastNative
private static native void sleep(Object lock, long millis, int nanos)throws...;
可见,线程池中使用的阻塞方式并不是Object中的wait(),也不是Thread.sleep() ;

这3个方法最终实现都是通过c&c++实现的native方法.

三.在<<Java虚拟机(第二版)>>中,对线程状态有以下介绍:
12.4.3状态转换
Java语言定义了5种线程状态,在任意一个时间点,一个线程只能有且只有其中的一种
状态,这5种状态分别如下。
1)新建(New):创建后尚未启动的线程处于这种状态。
2)运行(Runable):Runable包括了操作系统线程状态中的Running和Ready,也就是处于此
状态的线程有可能正在执行,也有可能正在等待着CPU为它分配执行时间。
3)无限期等待(Waiting):处于这种状态的线程不会被分配CPU执行时间,它们要等待被
其他线程显式地唤醒。以下方法会让线程陷入无限期的等待状态:
●没有设置Timeout参数的Object.wait()方法。
●没有设置Timeout参数的Thread.join()方法。
●LockSupport.park()方法。
4)限期等待(Timed Waiting):处于这种状态的线程也不会被分配CPU执行时间,不过无
须等待被其他线程显式地唤醒,在一定时间之后它们会由系统自动唤醒。以下方法会让线程
进入限期等待状态:
●Thread.sleep()方法。
●设置了Timeout参数的Object.wait()方法。
●设置了Timeout参数的Thread.join()方法。
●LockSupport.parkNanos()方法。
●LockSupport.parkUntil()方法。
5)阻塞(Blocked):线程被阻塞了,“阻塞状态”与“等待状态”的区别是:“阻塞状态”在等
待着获取到一个排他锁,这个事件将在另外一个线程放弃这个锁的时候发生;而“等待状
态”则是在等待一段时间,或者唤醒动作的发生。在程序等待进入同步区域的时候,线程将
进入这种状态。
结束(Terminated):已终止线程的线程状态,线程已经结束执行。

5. 你在阅读源代码或设计文档时,看到哪些惊艳的技巧

作为一个写Java c++的人,通过OOP进入骨头,我真的很佩服别人写的C代码。无论它是使用函数指针作为结构的参数来复制OOP,还是纯粹的程序化,都是书面的,所有函数都可以重入,它们不会相互影响,每次看到它们,你都会感到震惊。


上次偶然间看到的设计,本想和这位大神好好交流的,但是没有见到真人。最近在写一个RDB(Redis的mp文件)逆序列化的东西 看到一些作者节约存储空间的方式 比如 大多数情况下 key或value的长度都能维持在60左右 所以作者用来存储内容长度的字节默认只用6bit 另外2bit做标记 如果超出6bit能表达的长度 则用14bit表达 如果14bit表达不了 则索性用32bit表达 占用字节分别是 1、2、5(第一个字节用来标记接下来4个字节是实际长度) 根据内容的不同 存储的方式也不一样 比如普通的String类型 如果里面的字符全部都是数字 他会转换成整型去存 内容有一定长度后 进行lzf压缩 Redis的rdb还是相对简单的 没办法想象写mysql mp文件的逆序列化的人有多蛋疼。



6. 我的电脑里的java程序是不是重复了系统是64位的,那个后缀是24的是不是可以卸载掉呢谢谢

、 public、 internal 修饰符的访问权限。
答 . private : 私有成员, 在类的内部才可以访问。
protected : 保护成员,该类内部和继承类中可以访问。
public : 公共成员,完全公开,没有访问限制。
internal: 在同一命名空间内可以访问。
2 .列举ASP.NET 页面之间传递值的几种方式。
答. 1.使用QueryString, 如....?id=1; response. Redirect()....
2.使用Session变量
3.使用Server.Transfer

4.C#中的委托是什么?事件是不是一种委托?
答 : 委托可以把一个方法作为参数代入另一个方法。
委托可以理解为指向一个函数的引用。
是,是一种特殊的委托
5.override与重载的区别
答 :override 与重载的区别。重载是方法的名称相同。参数或参数类型不同,进行多
次重载以适应不同的需要
Override 是进行基类中函数的重写。为了适应需要。
6.如果在一个B/S结构的系统中需要传递变量值,但是又不能使用Session、
Cookie、Application,您有几种方法进行处理?
答 : this.Server.Transfer

9.描述一下C#中索引器的实现过程,是否只能根据数字进行索引?
答:不是。可以用任意类型。
11.用.net做B/S结构的系统,您是用几层结构来开发,每一层之间的关系以及为
什么要这样分层?
答:一般为3层
数据访问层,业务层,表示层。
数据访问层对数据库进行增删查改。
业务层一般分为二层,业务表观层实现与表示层的沟通,业务规则层实现用户密
码的安全等。
表示层为了与用户交互例如用户添加表单。
优点: 分工明确,条理清晰,易于调试,而且具有可扩展性。
缺点: 增加成本。
13.什么叫应用程序域?
答:应用程序域可以理解为一种轻量级进程。起到安全的作用。占用资源小。
14.CTS、CLS、CLR分别作何解释?
答:CTS:通用语言系统。CLS:通用语言规范。CLR:公共语言运行库。
15.什么是装箱和拆箱?
答:从值类型接口转换到引用类型装箱。从引用类型转换到值类型拆箱。
16.什么是受管制的代码?
答:unsafe:非托管代码。不经过CLR运行。
17.什么是强类型系统?
答:RTTI:类型识别系统。
18.net中读写数据库需要用到那些类?他们的作用?
答:DataSet:数据存储器。
DataCommand:执行语句命令
DataAdapter:数据的集合,用语填充。
19.ASP.net的身份验证方式有哪些?分别是什么原理?
答:10。Windwos(默认)用IIS...From(窗体)用帐户....Passport(密钥)
20.什么是Code-Behind技术?
答:代码后植。
21.在.net中,配件的意思是?
答:程序集。(中间语言,源数据,资源,装配清单)
22.常用的调用WebService的方法有哪些?
答:1.使用WSDL.exe命令行工具。
2.使用VS.NET中的Add Web Reference菜单选项
23..net Remoting 的工作原理是什么?
答:服务器端向客户端发送一个进程编号,一个程序域编号,以确定对象的位置

24.在C#中,string str = null 与 string str = “” 请尽量使用文字或图
象说明其中的区别。
答:string str = null 是不给他分配内存空间,而string str = "" 给它分配
长度为空字符串的内存空间。
25.请详述在dotnet中类(class)与结构(struct)的异同?
答:Class可以被实例化,属于引用类型,是分配在内存的堆上的,Struct属于值类
型,是分配在内存的栈上的.
27.分析以下代码,完成填空
string strTmp = "abcdefg某某某";
int i= System.Text.Encoding.Default.GetBytes(strTmp).Length;
int j= strTmp.Length;
以上代码执行完后,i= j=
答:i=13,j=10
28.SQLSERVER服务器中,给定表 table1 中有两个字段 ID、LastUpdateDate,
ID表示更新的事务号, LastUpdateDate表示更新时的服务器时间,请使用一句
SQL语句获得最后更新的事务号
答:Select ID FROM table1 Where LastUpdateDate = (Select MAX
(LastUpdateDate) FROM table1)
30.简要谈一下您对微软.NET 构架下remoting和webservice两项技术的理解以及
实际中的应用。
答:WS主要是可利用HTTP,穿透防火墙。而Remoting可以利用TCP/IP,二进制传
送提高效率。
31.公司要求开发一个继承System.Windows.Forms.ListView类的组件,要求达到
以下的特殊功能:点击ListView各列列头时,能按照点击列的每行值进行重排视
图中的所有行 (排序的方式如DataGrid相似)。根据您的知识,请简要谈一下您的
思路
答:根据点击的列头,包该列的ID取出,按照该ID排序后,在给绑定到ListView中。

33.写出一条Sql语句:取出表A中第31到第40记录(SQLServer,以自动增长的ID
作为主键,注意:ID可能不是连续的。
答:解1: select top 10 * from A where id not in (select top 30 id
from A)
解2: select top 10 * from A where id >(select max(id) from (select
top 30 id from A )as A)
34.面向对象的语言具有________性、_________性、________性
答:封装、继承、多态。
35.能用foreach遍历访问的对象需要实现 ________________接口或声明
________________方法的类型。
答:IEnumerable 、 GetEnumerator。
36.GC是什么? 为什么要有GC?
答:GC是垃圾收集器。程序员不用担心内存管理,因为垃圾收集器会自动进行管
理。要请求垃圾收集,可以调用下面的方法之一:
System.gc()
Runtime.getRuntime().gc()
37.String s = new String("xyz");创建了几个String Object?
答:两个对象,一个是“xyx”,一个是指向“xyx”的引用对象s。

39.启动一个线程是用run()还是start()?
答:启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状
态,这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。run()
方法可以产生必须退出的标志来停止一个线程。
40.接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可
继承实体类(concrete class)?
答:接口可以继承接口。抽象类可以实现(implements)接口,抽象类是否可继承
实体类,但前提是实体类必须有明确的构造函数。
41.构造器Constructor是否可被override?
答:构造器Constructor不能被继承,因此不能重写Overriding,但可以被重载
Overloading。
42.是否可以继承String类?
答:String类是final类故不可以继承。
43.try 里的code会
不会被执行,什么时候被执行,在return前还是后?
答:会执行,在return前执行。
44.两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句
话对不对?
答:不对,有相同的hash code。
45.swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上?
答:switch(expr1)中,expr1是一个整数表达式。因此传递给 switch 和
case 语句的参数应该是 int、 short、 char 或者 byte。long,string 都不
能作用于swtich。
47.当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此
对象的其它方法?
不能,一个对象的一个synchronized方法只能由一个线程访问。
48.abstract的method是否可同时是static,是否可同时是native,是否可同时是
synchronized?
答:都不能。
49.List, Set, Map是否继承自Collection接口?
答:List,Set是Map不是
50.Set里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是
equals()? 它们有何区别?
答:Set里的元素是不能重复的,那么用iterator()方法来区分重复与否。
equals()是判读两个Set是否相等。
equals()和==方法决定引用值是否指向同一对象equals()在类中被覆盖,为的是
当两个分离的对象的内容和类型相配的话,返回真值。
51.数组有没有length()这个方法? String有没有length()这个方法?
答:数组没有length()这个方法,有length的属性。String有有length()这个方
法。
52.sleep() 和 wait() 有什么区别?
答:sleep()方法是使线程停止一段时间的方法。在sleep 时间间隔期满后,线程
不一定立即恢复执行。这是因为在那个时刻,其它线程可能正在运行而且没有被
调度为放弃执行,除非(a)“醒来”的线程具有更高的优先级
(b)正在运行的线程因为其它原因而阻塞。
wait()是线程交互时,如果线程对一个同步对象x 发出一个wait()调用,该线程
会暂停执行,被调对象进入等待状态,直到被唤醒或等待时间到。
53.short s1 = 1; s1 = s1 + 1;有什么错? short s1 = 1; s1 += 1;有什么错
?
答:short s1 = 1; s1 = s1 + 1;有错,s1是short型,s1+1是int型,不能显式
转化为short型。可修改为s1 =(short)(s1 + 1) 。short s1 = 1; s1 += 1正
确。
54.谈谈final, finally, finalize的区别。
答:
final—修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新
的子类,不能作为父类被继承。因此 一个类不能既被声明为 abstract的,又被
声明为final的。将变量或方法声明为final,可以保证它们在使用中 不被改变。
被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可
修改。被声明为 final的方法也同样只能使用,不能重载
finally—再异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异
常,那么相匹配的 catch 子句就会 执行,然后控制就会进入 finally 块(如果
有的话)。
finalize—方法名。Java 技术允许使用 finalize() 方法在垃圾收集器将对象
从内存中清除出去之前做必要的清理 工作。这个方法是由垃圾收集器在确定这个
对象没有被引用时对这个对象调用的。它是在 Object 类中定义的 ,因此所有的
类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工
作。finalize() 方法是在垃圾收集器删除对象之前对这个对象调用的。
55.如何处理几十万条并发数据?
答:用存储过程或事务。取得最大标识的时候同时更新..注意主键不是自增量方
式这种方法并发的时候是不会有重复主键的..取得最大标识要有一个存储过程来
获取.
56.Session有什么重大BUG,微软提出了什么方法加以解决?
答:是iis中由于有进程回收机制,系统繁忙的话Session会丢失,可以用Sate
server或SQL Server数据库的方式存储Session不过这种方式比较慢,而且无法
捕获Session的END事件。
57.进程和线程的区别?
答:进程是系统进行资源分配和调度的单位;线程是CPU调度和分派的单位,一个
进程可以有多个线程,这些线程共享这个进程的资源。
58.堆和栈的区别?
答: 栈:由编译器自动分配、释放。在函数体中定义的变量通常在栈上。
堆:一般由程序员分配释放。用new、malloc等分配内存函数分配得到的就是在堆
上。
59.成员变量和成员函数前加static的作用?
答:它们被称为常成员变量和常成员函数,又称为类成员变量和类成员函数。分
别用来反映类的状态。比如类成员变量可以用来统计类实例的数量,类成员函数
负责这种统计的动作。
60.ASP。NET与ASP相比,主要有哪些进步?
答:asp解释形,aspx编译型,性能提高,有利于保护源码。
62.请说明在.net中常用的几种页面间传递参数的方法,并说出他们的优缺点。
答:session(viewstate) 简单,但易丢失
application 全局
cookie 简单,但可能不支持,可能被伪造
input ttype="hidden" 简单,可能被伪造
url参数 简单,显示于地址栏,长度有限
数据库 稳定,安全,但性能相对弱
63.请指出GAC的含义?
答:全局程序集缓存。
64.向服务器发送请求有几种方式?
答:get,post。get一般为链接方式,post一般为按钮方式。
65.DataReader与Dataset有什么区别?
答:一个是只能向前的只读游标,一个是内存中的表。
66.软件开发过程一般有几个阶段?每个阶段的作用?
答:需求分析,架构设计,代码编写,QA,部署
67.在c#中using和new这两个关键字有什么意义,请写出你所知道的意义?using
指令 和语句 new 创建实例 new 隐藏基类中方法。
答:using 引入名称空间或者使用非托管资源
new 新建实例或者隐藏父类方法
68.需要实现对一个字符串的处理,首先将该字符串首尾的空格去掉,如果字符串中
间还有连续空格的话,仅保留一个空格,即允许字符串中间有多个空格,但连续的空
格数不可超过一个.
答:string inputStr=" xx xx ";
inputStr=Regex.Replace(inputStr.Trim()," *"," ");

70.什么叫做SQL注入,如何防止?请举例说明。
答:利用sql关键字对网站进行攻击。过滤关键字'等
71.什么是反射?
答:动态获取程序集信息
72.用Singleton如何写设计模式
答:static属性里面new ,构造函数private
73.什么是Application Pool?
答:Web应用,类似Thread Pool,提高并发性能。
74.什么是虚函数?什么是抽象函数?
答:虚函数:没有实现的,可由子类继承并重写的函数。抽象函数:规定其非虚
子类必须实现的函数,必须被重写。
75.什么是XML?
答:XML即可扩展标记语言。eXtensible Markup Language.标记是指计算机所能
理解的信息符号,通过此种标记,计算机之间可以处理包含各种信息的文章等。
如何定义这些标记,即可以选择国际通用的标记语言,比如HTML,也可以使用象
XML这样由相关人士自由决定的标记语言,这就是语言的可扩展性。XML是从SGML
中简化修改出来的。它主要用到的有XML、XSL和XPath等。
76.什么是Web Service?UDDI?
答:Web Service便是基于网络的、分布式的模块化组件,它执行特定的任务,遵
守具体的技术规范,这些规范使得Web Service能与其他兼容的组件进行互操作。
UDDI 的目的是为电子商务建立标准;UDDI是一套基于Web的、分布式的、为
Web Service提供的、信息注册中心的实现标准规范,同时也包含一组使企业能将
自身提供的Web Service注册,以使别的企业能够发现的访问协议的实现标准。
77.什么是ASP.net中的用户控件?
答:用户控件一般用在内容多为静态,或者少许会改变的情况下..用的比较大..类
似ASP中的include..但是功能要强大的多。
78.列举一下你所了解的XML技术及其应用
答:xml用于配置,用于保存静态数据类型.接触XML最多的是web Services..和
config
79.ADO.net中常用的对象有哪些?分别描述一下。
答:Connection 数据库连接对象
Command 数据库命令
DataReader 数据读取器
DataSet 数据集
80.什么是code-Behind技术。
答:ASPX,RESX和CS三个后缀的文件,这个就是代码分离.实现了HTML代码和服务
器代码分离.方便代码编写和整理.
81.什么是SOAP,有哪些应用。
答:simple object access protocal,简单对象接受协议.以xml为基本编码结构
,建立在已有通信协议上(如http,不过据说ms在搞最底层的架构在tcp/ip上的
soap)的一种规范Web Service使用的协议..
82.C#中 property 与 attribute的区别,他们各有什么用处,这种机制的好处
在哪里?
答:一个是属性,用于存取类的字段,一个是特性,用来标识类,方法等的附加
性质
83.XML 与 HTML 的主要区别
答:1. XML是区分大小写字母的,HTML不区分。
2. 在HTML中,如果上下文清楚地显示出段落或者列表键在何处结尾,那么你可以
省略</p>或者</li>之类的结束 标记。在XML中,绝对不能省略掉结束标记。
3. 在XML中,拥有单个标记而没有匹配的结束标记的元素必须用一个 / 字符作为
结尾。这样分析器就知道不用 查找结束标记了。
4. 在XML中,属性值必须分装在引号中。在HTML中,引号是可用可不用的。
5. 在HTML中,可以拥有不带值的属性名。在XML中,所有的属性都必须带有相应
的值。
84.c#中的三元运算符是?
答:?:。
85.当整数a赋值给一个object对象时,整数a将会被?
答:装箱。
86.类成员有_____种可访问形式?
答:this.;new Class().Method;
87.public static const int A=1;这段代码有错误么?是什么?
答:const不能用static修饰。
88.float f=-123.567F; int i=(int)f;i的值现在是_____?
答:-123。
89.委托声明的关键字是______?
答:delegate.
90.用sealed修饰的类有什么特点?
答:密封,不能继承。
91.在Asp.net中所有的自定义用户控件都必须继承自________?
答:Control。
92.在.Net中所有可序列化的类都被标记为_____?
答:[serializable]
93.在.Net托管代码中我们不用担心内存漏洞,这是因为有了______?
答:GC。
95.当类T只声明了私有实例构造函数时,则在T的程序文本外部,___可以___(可
以 or 不可以)从T派生出新的类,不可以____(可以 or 不可以)直接创建T的
任何实例。
答:不可以,不可以。
96.下面这段代码有错误么?
switch (i){
case(): 答://case()条件不能为空
CaseZero();
break;
case 1:
CaseOne();
break;
case 2:
fault; 答://wrong,格式不正确
CaseTwo();
break;
}
97.在.Net中,类System.Web.UI.Page 可以被继承么?
答:可以。
98..net的错误处理机制是什么?
答:.net错误处理机制采用try->catch->finally结构,发生错误时,层层上抛
,直到找到匹配的Catch为止。
99.利用operator声明且仅声明了==,有什么错误么?
答:要同时修改Equale和GetHash() ? 重载了"==" 就必须重载 "!="
101.在.net(C# or vb.net)中如何取消一个窗体的关闭。
答:private void Form1_Closing(object sender,
System.ComponentModel.CancelEventArgs e)
{
e.Cancel=true;
}
102.在.net(C# or vb.net)中,Appplication.Exit 还是 Form.Close有什么
不同?
答:一个是退出整个应用程序,一个是关闭其中一个form。

104.某一密码仅使用K、L、M、N、O共5个字母,密码中的单词从左向右排列,密
码单词必须遵循如下规则:
(1) 密码单词的最小长度是两个字母,可以相同,也可以不同
(2) K不可能是单词的第一个字母
(3) 如果L出现,则出现次数不止一次
(4) M不能使最后一个也不能是倒数第二个字母
(5) K出现,则N就一定出现
(6) O如果是最后一个字母,则L一定出现
问题一:下列哪一个字母可以放在LO中的O后面,形成一个3个字母的密码单词?
A) K B)L C) M D) N
答案:B
问题二:如果能得到的字母是K、L、M,那么能够形成的两个字母长的密码单词的
总数是多少?
A)1个 B)3个 C)6个 D)9个
答案:A
问题三:下列哪一个是单词密码?
A) KLLN B) LOML C) MLLO D)NMKO
答案:C
8. 62-63=1 等式不成立,请移动一个数字(不可以移动减号和等于号),使得等
式成立,如何移动?
答案:62移动成2的6次方
106. C#中 property 与 attribute的区别,他们各有什么用处,这种机制的好
处在哪里?
答:attribute:自定义属性的基类;property :类中的属性
107.C#可否对内存进行直接的操作?
答:在.net下,.net引用了垃圾回收(GC)功能,它替代了程序员 不过在C#中,
不能直接实现Finalize方法,而是在析构函数中调用基类的Finalize()方法
108.ADO。NET相对于ADO等主要有什么改进?
答:1:ado.net不依赖于ole db提供程序,而是使用.net托管提供的程序,2:不使
用com3:不在支持动态游标和服务器端游 4:,可以断开connection而保留当前数
据集可用 5:强类型转换 6:xml支持
109.写一个HTML页面,实现以下功能,左键点击页面时显示“您好”,右键点击
时显示“禁止右键”。并在2分钟后自动关闭页面。
答:<script language=javascript>
setTimeout('window.close();',3000);
function show()
{
if (window.event.button == 1)
{
alert("左");
}
else if (window.event.button == 2)
{
alert("右");
}
}
</script>
110.大概描述一下ASP。NET服务器控件的生命周期
答:初始化 加载视图状态 处理回发数据 加载 发送回发更改通知 处理回发事件
预呈现 保存状态 呈现 处置 卸载
111.Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类,
是否可以implements(实现)interface(接口)?
答:不能,可以实现接口
112.Static Nested Class 和 Inner Class的不同,说得越多越好
答:Static Nested Class是被声明为静态(static)的内部类,它可以不依赖
于外部类实例被实例化。而通常的内部类需要在外部类实例化后才能实例化。
113.,&和&&的区别。
&是位运算符,表示按位与运算,&&是逻辑运算符,表示逻辑与(and).
114.HashMap和Hashtable的区别。
答:HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了
Map接口,主要区别在于HashMap允许空(null)键值(key),由于非线程安全,
效率上可能高于Hashtable.
116.Overloaded的方法是否可以改变返回值的类型?
答:Overloaded的方法是可以改变返回值的类型。
117.error和exception有什么区别?
答:error 表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存
溢出。不可能指望程序能处理这样的情况。
exception 表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从
不会发生的情况。
118.<%# %> 和 <% %> 有什么区别?
答:<%# %>表示绑定的数据源
<% %>是服务器端代码块
119.你觉得ASP.NET 2.0(VS2005)和你以前使用的开发工具(.Net 1.0或其他
)有什么最大的区别?你在以前的平台上使用的哪些开发思想(pattern /
architecture)可以移植到ASP.NET 2.0上 (或者已经内嵌在ASP.NET 2.0中)
答:1 ASP.NET 2.0 把一些代码进行了封装打包,所以相比1.0相同功能减少了很
多代码.
2 同时支持代码分离和页面嵌入服务器端代码两种模式,以前1.0版本,.NET提示帮
助只有在分离的代码文件,无 法 在页面嵌入服务器端代码获得帮助提示,
3 代码和设计界面切换的时候,2.0支持光标定位.这个我比较喜欢
4 在绑定数据,做表的分页.Update,Delete,等操作都可以可视化操作,方便了初
学者
5 在ASP.NET中增加了40多个新的控件,减少了工作量
120.重载与覆盖的区别?
答:1、方法的覆盖是子类和父类之间的关系,是垂直关系;方法的重载是同一个
类中方法之间的关系,是水平关系
2、覆盖只能由一个方法,或只能由一对方法产生关系;方法的重载是多个方法之
间的关系。
3、覆盖要求参数列表相同;重载要求参数列表不同。
4、覆盖关系中,调用那个方法体,是根据对象的类型(对象对应存储空间类型)
来决定;重载关系,是根据调 用时的实参表与形参表来选择方法体的。
121.描述一下C#中索引器的实现过程,是否只能根据数字进行索引?
答:不是。可以用任意类型。
122.在C#中,string str = null 与 string str = " " 请尽量使用文字或图
象说明其中的区别。
答:null是没有空间引用的;
" " 是空间为0的字符串;

126.公司要求开发一个继承System.Windows.Forms.ListView类的组件,要求达
到以下的特殊功能:点击ListView各列列头时,能按照点击列的每行值进行重排
视图中的所有行 (排序的方式如DataGrid相似)。根据您的知识,请简要谈一下您
的思路:
答:根据点击的列头,包该列的ID取出,按照该ID排序后,在给绑定到ListView中
127.什么是WSE?目前最新的版本是多少?
答:WSE (Web Service Extension) 包来提供最新的WEB服务安全保证,目前最
新版本2.0。
130.abstract class和interface有什么区别?
答:声明方法的存在而不去实现它的类被叫做抽象类(abstract class),它用
于要创建一个体现某些基本行为的类,并为该类声明方法,但不能在该类中实现
该类的情况。不能创建abstract 类的实例。然而可以创建一个变量,其类型是一
个抽象类,并让它指向具体子类的一个实例。不能有抽象构造函数或抽象静态方
法。Abstract 类的子类为它们父类中的所有抽象方法提供实现,否则它们也是抽
象类为。取而代之,在子类中实现该方法。知道其行为的其它类可以在类中实现
这些方法。
接口(interface)是抽象类的变体。在接口中,所有方法都是抽象的。多继承性
可通过实现这样的接口而获得。接口中的所有方法都是抽象的,没有一个有程序
体。接口只可以定义static final成员变量。接口的实现与子类相似,除了该实
现类不能从接口定义中继承行为。当类实现特殊接口时,它定义(即将程序体给
予)所有这种接口的方法。然后,它可以在实现了该接口的类的任何对象上调用
接口的方法。由于有抽象类,它允许使用接口名作为引用变量的类型。通常的动
态联编将生效。引用可以转换到接口类型或从接口类型转换,instanceof 运算符
可以用来决定某对象的类是否实现了接口。

7. java并发包源码怎么读

1. 各种同步控制工具的使用

1.1 ReentrantLock

ReentrantLock感觉上是synchronized的增强版,synchronized的特点是使用简单,一切交给JVM去处理,但是功能上是比较薄弱的。在JDK1.5之前,ReentrantLock的性能要好于synchronized,由于对JVM进行了优化,现在的JDK版本中,两者性能是不相上下的。如果是简单的实现,不要刻意去使用ReentrantLock。

相比于synchronized,ReentrantLock在功能上更加丰富,它具有可重入、可中断、可限时、公平锁等特点。

首先我们通过一个例子来说明ReentrantLock最初步的用法:

package test;

import java.util.concurrent.locks.ReentrantLock;public class Test implements Runnable{ public static ReentrantLock lock = new ReentrantLock(); public static int i = 0;

@Override public void run() { for (int j = 0; j < 10000000; j++)
{ lock.lock(); try
{
i++;
} finally
{ lock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
Test test = new Test();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}

}

有两个线程都对i进行++操作,为了保证线程安全,使用了ReentrantLock,从用法上可以看出,与synchronized相比,ReentrantLock就稍微复杂一点。因为必须在finally中进行解锁操作,如果不在finally解锁,有可能代码出现异常锁没被释放,而synchronized是由JVM来释放锁。

那么ReentrantLock到底有哪些优秀的特点呢?

1.1.1 可重入

单线程可以重复进入,但要重复退出

lock.lock();
lock.lock();try{
i++;

}
finally{
lock.unlock();
lock.unlock();
}

由于ReentrantLock是重入锁,所以可以反复得到相同的一把锁,它有一个与锁相关的获取计数器,如果拥有锁的某个线程再次得到锁,那么获取计数器就加1,然后锁需要被释放两次才能获得真正释放(重入锁)。这模仿了synchronized的语义;如果线程进入由线程已经拥有的监控器保护的 synchronized 块,就允许线程继续进行,当线程退出第二个(或者后续)synchronized块的时候,不释放锁,只有线程退出它进入的监控器保护的第一个synchronized块时,才释放锁。

public class Child extends Father implements Runnable{ final static Child child = new Child();//为了保证锁唯一
public static void main(String[] args) { for (int i = 0; i < 50; i++) { new Thread(child).start();
}
}
public synchronized void doSomething() {
System.out.println("1child.doSomething()");
doAnotherThing(); // 调用自己类中其他的synchronized方法
}
private synchronized void doAnotherThing() { super.doSomething(); // 调用父类的synchronized方法
System.out.println("3child.doAnotherThing()");
}
@Override
public void run() {
child.doSomething();
}
}class Father { public synchronized void doSomething() {
System.out.println("2father.doSomething()");
}
}

我们可以看到一个线程进入不同的synchronized方法,是不会释放之前得到的锁的。所以输出还是顺序输出。所以synchronized也是重入锁

输出:

1child.doSomething()
2father.doSomething()
3child.doAnotherThing()
1child.doSomething()
2father.doSomething()
3child.doAnotherThing()
1child.doSomething()
2father.doSomething()
3child.doAnotherThing()
...

1.1.2.可中断

与synchronized不同的是,ReentrantLock对中断是有响应的。中断相关知识查看[高并发Java 二] 多线程基础

普通的lock.lock()是不能响应中断的,lock.lockInterruptibly()能够响应中断。

我们模拟出一个死锁现场,然后用中断来处理死锁

package test;import java.lang.management.ManagementFactory;import java.lang.management.ThreadInfo;import java.lang.management.ThreadMXBean;import java.util.concurrent.locks.ReentrantLock;public class Test implements Runnable{ public static ReentrantLock lock1 = new ReentrantLock(); public static ReentrantLock lock2 = new ReentrantLock(); int lock; public Test(int lock)
{ this.lock = lock;
} @Override
public void run()
{ try
{ if (lock == 1)
{
lock1.lockInterruptibly(); try
{
Thread.sleep(500);
} catch (Exception e)
{ // TODO: handle exception
}
lock2.lockInterruptibly();
} else
{
lock2.lockInterruptibly(); try
{
Thread.sleep(500);
} catch (Exception e)
{ // TODO: handle exception
}
lock1.lockInterruptibly();
}
} catch (Exception e)
{ // TODO: handle exception
} finally
{ if (lock1.isHeldByCurrentThread())
{
lock1.unlock();
} if (lock2.isHeldByCurrentThread())
{
lock2.unlock();
}
System.out.println(Thread.currentThread().getId() + ":线程退出");
}
} public static void main(String[] args) throws InterruptedException {
Test t1 = new Test(1);
Test t2 = new Test(2);
Thread thread1 = new Thread(t1);
Thread thread2 = new Thread(t2);
thread1.start();
thread2.start();
Thread.sleep(1000); //DeadlockChecker.check();
} static class DeadlockChecker
{ private final static ThreadMXBean mbean = ManagementFactory
.getThreadMXBean(); final static Runnable deadlockChecker = new Runnable()
{ @Override
public void run()
{ // TODO Auto-generated method stub
while (true)
{ long[] deadlockedThreadIds = mbean.findDeadlockedThreads(); if (deadlockedThreadIds != null)
{
ThreadInfo[] threadInfos = mbean.getThreadInfo(deadlockedThreadIds); for (Thread t : Thread.getAllStackTraces().keySet())
{ for (int i = 0; i < threadInfos.length; i++)
{ if(t.getId() == threadInfos[i].getThreadId())
{
t.interrupt();
}
}
}
} try
{
Thread.sleep(5000);
} catch (Exception e)
{ // TODO: handle exception
}
}

}
};
public static void check()
{
Thread t = new Thread(deadlockChecker);
t.setDaemon(true);
t.start();
}
}

}

上述代码有可能会发生死锁,线程1得到lock1,线程2得到lock2,然后彼此又想获得对方的锁。

我们用jstack查看运行上述代码后的情况

下面举个例子:

package test;import java.util.concurrent.CyclicBarrier;public class Test implements Runnable{ private String soldier; private final CyclicBarrier cyclic; public Test(String soldier, CyclicBarrier cyclic)
{ this.soldier = soldier; this.cyclic = cyclic;
} @Override
public void run()
{ try
{ //等待所有士兵到齐
cyclic.await();
dowork(); //等待所有士兵完成工作
cyclic.await();
} catch (Exception e)
{ // TODO Auto-generated catch block
e.printStackTrace();
}

} private void dowork()
{ // TODO Auto-generated method stub
try
{
Thread.sleep(3000);
} catch (Exception e)
{ // TODO: handle exception
}
System.out.println(soldier + ": done");
} public static class BarrierRun implements Runnable
{ boolean flag; int n; public BarrierRun(boolean flag, int n)
{ super(); this.flag = flag; this.n = n;
} @Override
public void run()
{ if (flag)
{
System.out.println(n + "个任务完成");
} else
{
System.out.println(n + "个集合完成");
flag = true;
}

}

} public static void main(String[] args)
{ final int n = 10;
Thread[] threads = new Thread[n]; boolean flag = false;
CyclicBarrier barrier = new CyclicBarrier(n, new BarrierRun(flag, n));
System.out.println("集合"); for (int i = 0; i < n; i++)
{
System.out.println(i + "报道");
threads[i] = new Thread(new Test("士兵" + i, barrier));
threads[i].start();
}
}

}

打印结果:

集合

士兵5: done士兵7: done士兵8: done士兵3: done士兵4: done士兵1: done士兵6: done士兵2: done士兵0: done士兵9: done10个任务完成

1.7 LockSupport

提供线程阻塞原语

和suspend类似

LockSupport.park();
LockSupport.unpark(t1);

与suspend相比不容易引起线程冻结

LockSupport的思想呢,和Semaphore有点相似,内部有一个许可,park的时候拿掉这个许可,unpark的时候申请这个许可。所以如果unpark在park之前,是不会发生线程冻结的。

下面的代码是[高并发Java 二] 多线程基础中suspend示例代码,在使用suspend时会发生死锁。

而使用LockSupport则不会发生死锁。

另外

park()能够响应中断,但不抛出异常。中断响应的结果是,park()函数的返回,可以从Thread.interrupted()得到中断标志。

在JDK当中有大量地方使用到了park,当然LockSupport的实现也是使用unsafe.park()来实现的。

public static void park() { unsafe.park(false, 0L);
}

1.8 ReentrantLock 的实现

下面来介绍下ReentrantLock的实现,ReentrantLock的实现主要由3部分组成:

8. Java 的 DirectBuffer 是什么东西

那我们想知道 DirectBuffer 到底是怎么分配的?这些内存如何被回收?查看了 ByteBuffer.allocateDirect(int capacity) 方法的源代码,只有一行:?View CodeJAVA return new DirectByteBuffer(capacity); 就是创建一个 DirectByteBuffer 对象。我们再看 DirectByteBuffer 类的代码可以发现 DirectByteBuffer 的构造方法里调用一个本地(native)方法 allocateMemory 申请了一块指定大小的内存:?View CodeJAVA try { address = unsafe.allocateMemory(cap); } catch (OutOfMemoryError x) { …… } allocateMemory 是一个本地方法,功能就是做一些参数检查和字节对齐,然后使用系统调用 malloc 申请一块内存。从上面的分析也可以看出,创建一个 DirectByteBuffer 对象代价还是很大的,因为 malloc 系统调用代价就很大,需要 60 ~ 100 条 CPU 指令(一个 new Object() 只需要不足 10 条机器指令)。DirectByteBuffer 是如何回收这些内存的呢?DirectByteBuffer 类有一个内部的静态类 Deallocator,这个类实现了 Runnable 接口并在 run() 方法内释放了内存:?View CodeJAVA unsafe.freeMemory(address); 那这个 Deallocator 线程是哪里调用了呢?这里就用到了 Java 的虚引用(PhantomReference),Java 虚引用允许对象被回收之前做一些清理工作。在 DirectByteBuffer 的构造方法中创建了一个 Cleaner:?View CodeJAVA cleaner = Cleaner.create(this , new Deallocator(address, cap) ); 而Cleaner 类继承了 PhantomReference 类,并且在自己的 clean() 方法中启动了清理线程,当 DirectByteBuffer 被 GC 之前 cleaner 对象会被放入一个引用队列(ReferenceQueue),JVM 会启动一个低优先级线程扫描这个队列,并且执行 Cleaner 的 clean 方法来做清理工作。OK,DirectByteBuffer 的实现大概搞清楚了,那我们是否该在自己的代码中使用 DirectByteBuffer 呢?我个人认为可以适当的使用,使用直接内存确实避免了 GC 问题和内存拷贝的问题,但是我们不得不考虑两个问题:1)操作系统可能会把 DirectByteBuffer 的内存交换到磁盘上,这样势必会影响性能,为了避免这个问题我们不得不对操作系统做相应的配置;2)DirectByteBuffer 申请内存失败会直接抛出 OutOfMemoryError,对于这种情况,还是要想办法处理。

阅读全文

与javaunsafe源码相关的资料

热点内容
喷油螺杆制冷压缩机 浏览:577
python员工信息登记表 浏览:375
高中美术pdf 浏览:158
java实现排列 浏览:511
javavector的用法 浏览:979
osi实现加密的三层 浏览:230
大众宝来原厂中控如何安装app 浏览:911
linux内核根文件系统 浏览:240
3d的命令面板不见了 浏览:524
武汉理工大学服务器ip地址 浏览:146
亚马逊云服务器登录 浏览:522
安卓手机如何进行文件处理 浏览:70
mysql执行系统命令 浏览:928
php支持curlhttps 浏览:142
新预算法责任 浏览:443
服务器如何处理5万人同时在线 浏览:249
哈夫曼编码数据压缩 浏览:424
锁定服务器是什么意思 浏览:383
场景检测算法 浏览:616
解压手机软件触屏 浏览:348