PS:还有几天就开学了.先来一发.

学习内容:

1.序列化的目的

2.Android中序列化的两种方式

3.Parcelable与Serializable的性能比较

4.Android中如何使用Parcelable进行序列化操作

5.Parcelable的工作原理

6.相关实例

1.序列化的目的

/* Style Definitions */
table.MsoNormalTable
{mso-style-name:普通表格;
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-priority:99;
mso-style-qformat:yes;
mso-style-parent:"";
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin:0cm;
mso-para-margin-bottom:.0001pt;
mso-pagination:widow-orphan;
font-size:10.0pt;
font-family:"Calibri","sans-serif";
mso-bidi-font-family:"Times New Roman";}

        1.永久的保存对象数据(将对象数据保存在文件当中,或者是磁盘中

2.通过序列化操作将对象数据在网络上进行传输(由于网络传输是以字节流的方式对数据进行传输的.因此序列化的目的是将对象数据转换成字节流的形式)

3.将对象数据在进程之间进行传递(Activity之间传递对象数据时,需要在当前的Activity中对对象数据进行序列化操作.在另一个Activity中需要进行反序列化操作讲数据取出)

4.Java平台允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在,即,这些对象的生命周期不会比JVM的生命周期更长(即每个对象都在JVM中)但在现实应用中,就可能要停止JVM运行,但有要保存某些指定的对象,并在将来重新读取被保存的对象。这是Java对象序列化就能够实现该功能。(可选择入数据库、或文件的形式保存)

5.序列化对象的时候只是针对变量进行序列化,不针对方法进行序列化.

6.在Intent之间,基本的数据类型直接进行相关传递即可,但是一旦数据类型比较复杂的时候,就需要进行序列化操作了.

2.Android中实现序列化的两种方式

  1.Implements Serializable 接口 (声明一下即可)

Serializable 的简单实例:

public class Person implements Serializable{
private static final long serialVersionUID = -7060210544600464481L;
private String name;
private int age; public String getName(){
return name;
} public void setName(String name){
this.name = name;
} public int getAge(){
return age;
} public void setAge(int age){
this.age = age;
}
}

  2.Implements Parcelable 接口(不仅仅需要声明,还需要实现内部的相应方法)

Parcelable的简单实例:

注:写入数据的顺序和读出数据的顺序必须是相同的.
public class Book implements Parcelable{
private String bookName;
private String author;
private int publishDate; public Book(){ } public String getBookName(){
return bookName;
} public void setBookName(String bookName){
this.bookName = bookName;
} public String getAuthor(){
return author;
} public void setAuthor(String author){
this.author = author;
} public int getPublishDate(){
return publishDate;
} public void setPublishDate(int publishDate){
this.publishDate = publishDate;
} @Override
public int describeContents(){
return 0;
} @Override
public void writeToParcel(Parcel out, int flags){
out.writeString(bookName);
out.writeString(author);
out.writeInt(publishDate);
} public static final Parcelable.Creator<Book> CREATOR = new Creator<Book>(){

     @Override
public Book[] newArray(int size){
return new Book[size];
} @Override
public Book createFromParcel(Parcel in){
return new Book(in);
}
}; public Book(Parcel in){
//如果元素数据是list类型的时候需要: lits = new ArrayList<?> in.readList(list); 否则会出现空指针异常.并且读出和写入的数据类型必须相同.如果不想对部分关键字进行序列化,可以使用transient关键字来修饰以及static修饰.
bookName = in.readString();
author = in.readString();
publishDate = in.readInt();
}
}

  我们知道在Java应用程序当中对类进行序列化操作只需要实现Serializable接口就可以,由系统来完成序列化和反序列化操作,但是在Android中序列化操作有另外一种方式来完成,那就是实现Parcelable接口.也是Android中特有的接口来实现类的序列化操作.原因是Parcelable的性能要强于Serializable.因此在绝大多数的情况下,Android还是推荐使用Parcelable来完成对类的序列化操作的.

3.Parcelable与Serializable的性能比较

  首先Parcelable的性能要强于Serializable的原因我需要简单的阐述一下

  1. 在内存的使用中,前者在性能方面要强于后者

  2. 后者在序列化操作的时候会产生大量的临时变量,(原因是使用了反射机制)从而导致GC的频繁调用,因此在性能上会稍微逊色

  3. Parcelable是以Ibinder作为信息载体的.在内存上的开销比较小,因此在内存之间进行数据传递的时候,Android推荐使用Parcelable,既然是内存方面比价有优势,那么自然就要优先选择.

  4. 在读写数据的时候,Parcelable是在内存中直接进行读写,而Serializable是通过使用IO流的形式将数据读写入在硬盘上.

  但是:虽然Parcelable的性能要强于Serializable,但是仍然有特殊的情况需要使用Serializable,而不去使用Parcelable,因为Parcelable无法将数据进行持久化,因此在将数据保存在磁盘的时候,仍然需要使用后者,因为前者无法很好的将数据进行持久化.(原因是在不同的Android版本当中,Parcelable可能会不同,因此数据的持久化方面仍然是使用Serializable)

  速度测试:

/* Style Definitions */
table.MsoNormalTable
{mso-style-name:普通表格;
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-priority:99;
mso-style-qformat:yes;
mso-style-parent:"";
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin:0cm;
mso-para-margin-bottom:.0001pt;
mso-pagination:widow-orphan;
font-size:10.0pt;
font-family:"Calibri","sans-serif";
mso-bidi-font-family:"Times New Roman";}

  测试方法:

  • 通过将一个对象放到一个bundle里面然后调用Bundle#writeToParcel(Parcel,
    int)
    方法来模拟传递对象给一个activity的过程,然后再把这个对象取出来。
  • 在一个循环里面运行1000 次。
  • 两种方法分别运行10次来减少内存整理,cpu被其他应用占用等情况的干扰。
  • 参与测试的对象就是上面的相关代码
  • 在多种Android软硬件环境上进行测试
    • LG Nexus 4 – Android
      4.2.2
    • Samsung Nexus 10 –
      Android 4.2.2
    • HTC Desire Z – Android
      2.3.3

  结果如图:

 

 

Normal
0

7.8 磅
0
2

false
false
false

EN-US
ZH-CN
X-NONE

MicrosoftInternetExplorer4

/* Style Definitions */
table.MsoNormalTable
{mso-style-name:普通表格;
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-priority:99;
mso-style-qformat:yes;
mso-style-parent:"";
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin:0cm;
mso-para-margin-bottom:.0001pt;
mso-pagination:widow-orphan;
font-size:10.0pt;
font-family:"Calibri","sans-serif";
mso-bidi-font-family:"Times New Roman";}

  性能差异:

    Nexus 10

    Serializable:
1.0004ms,  Parcelable: 0.0850ms –
提升10.16倍。

  Nexus 4

  Serializable:
1.8539ms – Parcelable: 0.1824ms –
提升11.80倍。

  Desire Z

  Serializable:
5.1224ms – Parcelable: 0.2938ms –
提升17.36倍。

   由此可以得出:
Parcelable 比 Serializable快了10多倍。

  从相对的比较我们可以看出,Parcelable的性能要比Serializable要优秀的多,因此在Android中进行序列化操作的时候,我们需要尽可能的选择前者,需要花上大量的时间去实现Parcelable接口中的内部方法.

4.Android中如何使用Parcelable进行序列化操作

  说了这么多,我们还是来看看Android中如何去使用Parcelable实现类的序列化操作吧.

Normal
0

7.8 磅
0
2

false
false
false

EN-US
ZH-CN
X-NONE

MicrosoftInternetExplorer4

/* Style Definitions */
table.MsoNormalTable
{mso-style-name:普通表格;
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-priority:99;
mso-style-qformat:yes;
mso-style-parent:"";
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin:0cm;
mso-para-margin-bottom:.0001pt;
mso-pagination:widow-orphan;
font-size:10.0pt;
font-family:"Calibri","sans-serif";
mso-bidi-font-family:"Times New Roman";}

   Implements Parcelable的时候需要实现内部的方法:

  1.writeToParcel 将对象数据序列化成一个Parcel对象(序列化之后成为Parcel对象.以便Parcel容器取出数据)

  2.重写describeContents方法,默认值为0

  3.Public static final Parcelable.Creator<T>CREATOR (将Parcel容器中的数据转换成对象数据) 同时需要实现两个方法:
  3.1 CreateFromParcel(从Parcel容器中取出数据并进行转换.)
  3.2 newArray(int size)返回对象数据的大小

  因此,很明显实现Parcelable并不容易。实现Parcelable接口需要写大量的模板代码,这使得对象代码变得难以阅读和维护。具体的实例就是上面Parcelable的实例代码.就不进行列举了.(有兴趣的可以去看看Android中NetWorkInfo的源代码,是关于网络连接额外信息的一个相关类,内部就实现了序列化操作.大家可以去看看)

5.Parcelable的工作原理

/* Style Definitions */
table.MsoNormalTable
{mso-style-name:普通表格;
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-priority:99;
mso-style-qformat:yes;
mso-style-parent:"";
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin:0cm;
mso-para-margin-bottom:.0001pt;
mso-pagination:widow-orphan;
font-size:10.0pt;
font-family:"Calibri","sans-serif";
mso-bidi-font-family:"Times New Roman";}

  无论是对数据的读还是写都需要使用Parcel作为中间层将数据进行传递.Parcel涉及到的东西就是与C++底层有关了.都是使用JNI.在Java应用层是先创建Parcel(Java)对象,然后再调用相关的读写操作的时候.就拿读写32为Int数据来说吧:

static jint android_os_Parcel_readInt(JNIEnv* env, jobject clazz){
Parcel* parcel = parcelForJavaObject(env, clazz);
if (parcel != NULL) {
return parcel->readInt32();
}
return 0;
}

Normal
0

7.8 磅
0
2

false
false
false

EN-US
ZH-CN
X-NONE

MicrosoftInternetExplorer4

/* Style Definitions */
table.MsoNormalTable
{mso-style-name:普通表格;
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-priority:99;
mso-style-qformat:yes;
mso-style-parent:"";
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin:0cm;
mso-para-margin-bottom:.0001pt;
mso-pagination:widow-orphan;
font-size:10.0pt;
font-family:"Calibri","sans-serif";
mso-bidi-font-family:"Times New Roman";}

/* Style Definitions */
table.MsoNormalTable
{mso-style-name:普通表格;
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-priority:99;
mso-style-qformat:yes;
mso-style-parent:"";
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin:0cm;
mso-para-margin-bottom:.0001pt;
mso-pagination:widow-orphan;
font-size:10.0pt;
font-family:"Calibri","sans-serif";
mso-bidi-font-family:"Times New Roman";}

调用的方法就是这个过程,首先是将Parcel(Java)对象转换成Parcel(C++)对象,然后被封装在Parcel中的相关数据由C++底层来完成数据的序列化操作.

status_t Parcel::writeInt32(int32_t val){
return writeAligned(val);
}
template<class t="">
status_t Parcel::writeAligned(T val) {
COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(sizeof(T)) == sizeof(T)); if ((mDataPos+sizeof(val)) <= mDataCapacity) {
restart_write:
*reinterpret_cast<t*>(mData+mDataPos) = val;
return finishWrite(sizeof(val));
} status_t err = growData(sizeof(val));
if (err == NO_ERROR) goto restart_write;
return err;
}

真正的读写过程是由下面的源代码来完成的.

status_t Parcel::continueWrite(size_t desired)
{
// If shrinking, first adjust for any objects that appear
// after the new data size.
size_t objectsSize = mObjectsSize;
if (desired < mDataSize) {
if (desired == ) {
objectsSize = ;
} else {
while (objectsSize > ) {
if (mObjects[objectsSize-] < desired)
break;
objectsSize--;
}
}
} if (mOwner) {
// If the size is going to zero, just release the owner's data.
if (desired == ) {
freeData();
return NO_ERROR;
} // If there is a different owner, we need to take
// posession.
uint8_t* data = (uint8_t*)malloc(desired);
if (!data) {
mError = NO_MEMORY;
return NO_MEMORY;
}
size_t* objects = NULL; if (objectsSize) {
objects = (size_t*)malloc(objectsSize*sizeof(size_t));
if (!objects) {
mError = NO_MEMORY;
return NO_MEMORY;
} // Little hack to only acquire references on objects
// we will be keeping.
size_t oldObjectsSize = mObjectsSize;
mObjectsSize = objectsSize;
acquireObjects();
mObjectsSize = oldObjectsSize;
} if (mData) {
memcpy(data, mData, mDataSize < desired ? mDataSize : desired);
}
if (objects && mObjects) {
memcpy(objects, mObjects, objectsSize*sizeof(size_t));
}
//ALOGI("Freeing data ref of %p (pid=%d)\n", this, getpid());
mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie);
mOwner = NULL; mData = data;
mObjects = objects;
mDataSize = (mDataSize < desired) ? mDataSize : desired;
ALOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
mDataCapacity = desired;
mObjectsSize = mObjectsCapacity = objectsSize;
mNextObjectHint = ; } else if (mData) {
if (objectsSize < mObjectsSize) {
// Need to release refs on any objects we are dropping.
const sp<ProcessState> proc(ProcessState::self());
for (size_t i=objectsSize; i<mObjectsSize; i++) {
const flat_binder_object* flat
= reinterpret_cast<flat_binder_object*>(mData+mObjects[i]);
if (flat->type == BINDER_TYPE_FD) {
// will need to rescan because we may have lopped off the only FDs
mFdsKnown = false;
}
release_object(proc, *flat, this);
}
size_t* objects =
(size_t*)realloc(mObjects, objectsSize*sizeof(size_t));
if (objects) {
mObjects = objects;
}
mObjectsSize = objectsSize;
mNextObjectHint = ;
} // We own the data, so we can just do a realloc().
if (desired > mDataCapacity) {
uint8_t* data = (uint8_t*)realloc(mData, desired);
if (data) {
mData = data;
mDataCapacity = desired;
} else if (desired > mDataCapacity) {
mError = NO_MEMORY;
return NO_MEMORY;
}
} else {
if (mDataSize > desired) {
mDataSize = desired;
ALOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
}
if (mDataPos > desired) {
mDataPos = desired;
ALOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos);
}
} } else {
// This is the first data. Easy!
uint8_t* data = (uint8_t*)malloc(desired);
if (!data) {
mError = NO_MEMORY;
return NO_MEMORY;
} if(!(mDataCapacity == && mObjects == NULL
&& mObjectsCapacity == )) {
ALOGE("continueWrite: %d/%p/%d/%d", mDataCapacity, mObjects, mObjectsCapacity, desired);
} mData = data;
mDataSize = mDataPos = ;
ALOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
ALOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos);
mDataCapacity = desired;
} return NO_ERROR;
}

   1.整个读写全是在内存中进行,主要是通过malloc()、realloc()、memcpy()等内存操作进行,所以效率比JAVA序列化中使用外部存储器会高很多

   2.读写时是4字节对齐的,可以看到#define PAD_SIZE(s) (((s)+3)&~3)这句宏定义就是在做这件事情

   3.如果预分配的空间不够时newSize = ((mDataSize+len)*3)/2;会一次多分配50%

   4.对于普通数据,使用的是mData内存地址,对于IBinder类型的数据以及FileDescriptor使用的是mObjects内存地址。后者是通过flatten_binder()和unflatten_binder()实现的,目的是反序列化时读出的对象就是原对象而不用重新new一个新对象。
6.相关实例

  最后上一个例子..

  首先是序列化的类Book.class

public class Book implements Parcelable{
private String bookName;
private String author;
private int publishDate; public Book(){ } public String getBookName(){
return bookName;
} public void setBookName(String bookName){
this.bookName = bookName;
} public String getAuthor(){
return author;
} public void setAuthor(String author){
this.author = author;
} public int getPublishDate(){
return publishDate;
} public void setPublishDate(int publishDate){
this.publishDate = publishDate;
} @Override
public int describeContents(){
return 0;
} @Override
public void writeToParcel(Parcel out, int flags){
out.writeString(bookName);
out.writeString(author);
out.writeInt(publishDate);
} public static final Parcelable.Creator<Book> CREATOR = new Creator<Book>(){      @Override
public Book[] newArray(int size){
return new Book[size];
} @Override
public Book createFromParcel(Parcel in){
return new Book(in);
}
}; public Book(Parcel in){
//如果元素数据是list类型的时候需要: lits = new ArrayList<?> in.readList(list); 否则会出现空指针异常.并且读出和写入的数据类型必须相同.如果不想对部分关键字进行序列化,可以使用transient关键字来修饰以及static修饰.
bookName = in.readString();
author = in.readString();
publishDate = in.readInt();
}
}

第一个Activity,MainActivity

Book book = new Book();
book.setBookname("Darker");
book.setBookauthor("me");
book.setPublishDate(20);
Bundle bundle = new Bundle();
bundle.putParcelable("book", book);
Intent intent = new Intent(MainActivity.this,AnotherActivity.class);
intent.putExtras(bundle);

第二个Activity,AnotherActivity

Intent intent = getIntent();
Bundle bun = intent.getExtras();
Book book = bun.getParcelable("book");
System.out.println(book);

 总结:Java应用程序中有Serializable来实现序列化操作,Android中有Parcelable来实现序列化操作,相关的性能也作出了比较,因此在Android中除了对数据持久化的时候需要使用到Serializable来实现序列化操作,其他的时候我们仍然需要使用Parcelable来实现序列化操作,因为在Android中效率并不是最重要的,而是内存,通过比较Parcelable在效率和内存上都要优秀与Serializable,尽管Parcelable实现起来比较复杂,但是如果我们想要成为一名优秀的Android软件工程师,那么我们就需要勤快一些去实现Parcelable,而不是偷懒与实现Serializable.当然实现后者也不是不行,关键在于我们头脑中的那一份思想.

/* Style Definitions */
table.MsoNormalTable
{mso-style-name:普通表格;
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-priority:99;
mso-style-qformat:yes;
mso-style-parent:"";
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin:0cm;
mso-para-margin-bottom:.0001pt;
mso-pagination:widow-orphan;
font-size:10.0pt;
font-family:"Calibri","sans-serif";
mso-bidi-font-family:"Times New Roman";}

Android序列化之Serializable和Parcelable的更多相关文章

  1. Android开发之Serializable 和 Parcelable的差别(源码分享)

    android 中自己定义的对象序列化的问题有两个选择一个是Parcelable,另外一个是Serializable. 一 序列化原因: 1.永久性保存对象.保存对象的字节序列到本地文件里. 2.通过 ...

  2. Android中Serializable和Parcelable序列化对象详解

    学习内容: 1.序列化的目的 2.Android中序列化的两种方式 3.Parcelable与Serializable的性能比较 4.Android中如何使用Parcelable进行序列化操作 5.P ...

  3. android序列化(1)Parcelable与Serializable

    1.Android中实现序列化有两个选择 一是实现Serializable接口(是JavaSE本身就支持的),实现Serializable接口非常简单. 一是实现Parcelable接口(是Andro ...

  4. 【Android - IPC】之Serializable和Parcelable序列化

    1.序列化的目的 (1)永久的保存对象数据(将对象数据保存到文件或磁盘中): (2)通过序列化操作将对象数据在网络上进行传输(由于网络传输是以字节流的方式对数据进行传输的,因此序列化的目的是将对象数据 ...

  5. android Activity之间数据传递 Parcelable和Serializable接口的使用

    Activity之间传数据时,为了避免麻烦,往往会将一些值封装成对象,然后将整个对象传递过去.传对象的时候有两种情况,一种是实现Parcelable接口,一种是实现Serializable接口.0.解 ...

  6. 浅谈Android中Serializable和Parcelable使用区别

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 一.概述 Android开发的时候,我们时长遇到传递对象的需求,但是我们无法将对象的引用传给Activity或者Fragment,我们需要将这些对象 ...

  7. 序列化Serializable和Parcelable

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 简单记录下序列化Serializable和Parcelable的使用方法. Android中Intent如果要传递类对象,可以通过两 ...

  8. 转 序列化Serializable和Parcelable的区别详解

    什么是序列化,为什么要进行序列化 答:对象要进行传输(如:activity 与activity间 ,网络间 进程间等等).存储到本地就必须进行序列化 . 这种可传输的状态就是序列化. 怎么序列化??两 ...

  9. 对 Serializable和Parcelable理解

    1.首先他们两个接口都是为了实现对象的序列化,使之可以传递,所谓序列化就是将对象信息装换成可以存储的介质的过程. 2.Serializable是jdk所提供的序列化接口,该接口存在于io包下,可想用于 ...

随机推荐

  1. linux指令之文件的创建、查询、修改

    mkdir(make directory) 功能:创建目录 案例: mkdir test 点评:将创建一个目录名为test的目录 rmdir(remove directory) 功能:删除目录 案例: ...

  2. label正确显示多行数据

    label显示多行文字时会遇到文字中包含换行符“\n”,这时需要对字符进行全部替换, if(labelContent.indexOf('\\n')>0){labelContent=labelCo ...

  3. http://blog.sina.com.cn/s/blog_4c3b6a070100etad.html

    http://blog.sina.com.cn/s/blog_4c3b6a070100etad.html

  4. java 并发性和多线程 -- 读感 (二 线程间通讯,共享内存的机制)

    参考文章:http://ifeve.com/java-concurrency-thread-directory/ 其中的竞态,线程安全,内存模型,线程间的通信,java ThreadLocal类小节部 ...

  5. Entity FrameWork 中使用Lambda访问数据库性能优化

    在使用Entity Framework 访问数据库时,我们经常使用Lambda表达式,但是如果不小心的话,很容易就掉到坑里了.比如下面的例子:用Lambda访问MSSqlServer中的NewsInf ...

  6. 控件UI性能调优 -- SizeChanged不是万能的

    简介 我们在之前的“UWP控件开发——用NuGet包装自己的控件“一文中曾提到XAML的布局系统 和平时使用上的一些问题(重写Measure/Arrange还是使用SizeChanged?),这篇博文 ...

  7. 使用SQLIO测试磁盘性能

    SQLIO 是一个用于测试存储系统能力的命令行工具,用以获取存储系统相关的性能指标,以判断系统的 I/O 处理能力. 在微软的网站可以下载 SQLIO 的安装包,安装后目录中会出现如下文件: EULA ...

  8. No resource found that matches the given name 'android:Widget.Material.A解决方案

    1:首先新建空白工作区 2:先import appcompat_v7 appcompat_v7在一个类似这样的地方, C:\mywork\android\android-sdk-windows\ext ...

  9. Ubuntu环境搭建系列—WPS/LAMP/Python篇

    由于篇幅不宜太长,所以就将此文分成了两部分,在第一部分中我们讲解了google-chrome(谷歌浏览器)的安装,jdk的环境配置,android开发的环境搭建.那么在第二部分中,位们将讲解的是金山W ...

  10. 将不确定变为确定~DateTime.MinValue和MaxValue引发的异常

    回到目录 问题描述: SqlDateTime 溢出.必须介于 1/1/1753 12:00:00 AM 和 12/31/9999 11:59:59 PM 之间 概念相关 .Net中的DateTime结 ...