.NET的深复制方法(以C#语言为例)
作者:北风其凉 发布时间:2022-07-18 00:17:05
很多时候我们复制一个对象实例A到实例B,在用实例B去做其他事情的时候,会对实例B进行修改,为保证对B的修改不会影响到A的正常使用,就需要使用到深复制。
我在网上搜到一些深复制的方法,同时写了几组例子对这些方法进行测试。
我的操作系统版本为Win7旗舰版,.NET Framework版本是4.5
测试程序
我建了一个C#窗体应用程序(Winform),其主窗口FormMain的Load函数内容如下:
private void FormMain_Load(object sender, EventArgs e)
{
//测试1:深度复制 自定义类
try
{
Console.WriteLine("=== 深度复制 自定义类 ===");
TestClass test1 = new TestClass();
test1.a = 10;
test1.b = "hello world!";
test1.c = new string[] { "x", "y", "z" };
TestClass test2 = new TestClass();
test2.a = 11;
test2.b = "hello world2!";
test2.c = new string[] { "i", "j", "k" };
test1.d = test2;
Console.WriteLine("---test1_start---");
Console.WriteLine(test1);
Console.WriteLine("---test1_end---");
TestClass test3 = (TestClass)DataManHelper.DeepCopyObject(test1);
Console.WriteLine("---test3_start---");
Console.WriteLine(test3);
Console.WriteLine("---test3_end---");
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
//测试2:深度复制 可序列化的自定义类
try
{
Console.WriteLine("=== 深度复制 可序列化的自定义类 ===");
TestClassWithS test1 = new TestClassWithS();
test1.a = 10;
test1.b = "hello world!";
test1.c = new string[] { "x", "y", "z" };
TestClassWithS test2 = new TestClassWithS();
test2.a = 11;
test2.b = "hello world2!";
test2.c = new string[] { "i", "j", "k" };
test1.d = test2;
Console.WriteLine("---test1_start---");
Console.WriteLine(test1);
Console.WriteLine("---test1_end---");
TestClassWithS test3 = (TestClassWithS)DataManHelper.DeepCopyObject(test1);
Console.WriteLine("---test3_start---");
Console.WriteLine(test3);
Console.WriteLine("---test3_end---");
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
//测试3:深度复制 DataTable
try
{
Console.WriteLine("=== 深度复制 DataTable ===");
DataTable dtKirov = new DataTable("TestTable");
dtKirov.Columns.Add("Col1");
dtKirov.Columns.Add("Col2");
dtKirov.Columns.Add("Col3");
dtKirov.Rows.Add("1-1", "1-2", "1-3");
dtKirov.Rows.Add("2-1", "2-2", "2-3");
dtKirov.Rows.Add("3-1", "3-2", "3-3");
Console.WriteLine("=== 复制前 ===");
for (int i = 0; i < dtKirov.Columns.Count; i++)
{
Console.Write(dtKirov.Columns[i].ColumnName + "\t");
}
Console.WriteLine("\n-----------------");
for (int i = 0; i < dtKirov.Columns.Count; i++)
{
for (int j = 0; j < dtKirov.Rows.Count; j++)
{
Console.Write(dtKirov.Rows[i][j].ToString() + "\t");
}
Console.WriteLine();
}
Console.WriteLine();
DataTable dtDreadNought = (DataTable)DataManHelper.DeepCopyObject(dtKirov);
Console.WriteLine("=== 复制后 ===");
for (int i = 0; i < dtDreadNought.Columns.Count; i++)
{
Console.Write(dtDreadNought.Columns[i].ColumnName + "\t");
}
Console.WriteLine("\n-----------------");
for (int i = 0; i < dtDreadNought.Columns.Count; i++)
{
for (int j = 0; j < dtDreadNought.Rows.Count; j++)
{
Console.Write(dtDreadNought.Rows[i][j].ToString() + "\t");
}
Console.WriteLine();
}
Console.WriteLine();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
//测试4:深度复制 TextBox
try
{
Console.WriteLine("=== 深度复制 TextBox ===");
txtTest.Text = "1234";
Console.WriteLine("复制前:" + txtTest.Text);
TextBox txtTmp = new TextBox();
txtTmp = (TextBox)DataManHelper.DeepCopyObject(txtTest);
Console.WriteLine("复制后:" + txtTmp.Text);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
//测试5:深度复制 DataGridView
try
{
Console.WriteLine("=== 深度复制 DataGridView ===");
DataGridView dgvTmp = new DataGridView();
dgvTmp = (DataGridView)DataManHelper.DeepCopyObject(dgvTest);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
其中txtTest是一个测试用的TextBox,dgvTmp是一个测试用的DataGridView,TestClass是一个自定义类,TestClassWithS是添加了Serializable特性的TestClass类,它们的具体实现如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DataCopyTest
{
public class TestClass
{
public int a;
public string b;
public string[] c;
public TestClass d;
public override string ToString()
{
string s = "a:" + a + "\n";
if (b != null)
{
s += "b:" + b + "\n";
}
if (c != null)
{
foreach (string tmps in c)
{
if (!string.IsNullOrWhiteSpace(tmps))
{
s += "c:" + tmps + "\n";
}
}
}
if (d != null)
{
s += d.ToString();
}
return s;
}
}
//支持序列化的TestClass
[Serializable]
public class TestClassWithS
{
public int a;
public string b;
public string[] c;
public TestClassWithS d;
public override string ToString()
{
string s = "a:" + a + "\n";
if (b != null)
{
s += "b:" + b + "\n";
}
if (c != null)
{
foreach (string tmps in c)
{
if (!string.IsNullOrWhiteSpace(tmps))
{
s += "c:" + tmps + "\n";
}
}
}
if (d != null)
{
s += d.ToString();
}
return s;
}
}
}
我对每个搜来的深复制方法,都用了这五个类的实例进行深复制测试,这五个类的特征如下:
I、对自定义类TestClass进行深复制测试
II、对自定义类TestClassWithS进行深复制测试,TestClassWithS是添加了Serializable特性的TestClass类
III、对DataTable进行深复制测试
IV、对控件TextBox进行深复制测试
V、对控件DataGridView进行深复制测试
我们通过实现方法DataManHelper.DeepCopyObject来进行测试
测试深复制方法1
使用二进制流的序列化与反序列化深度复制对象
public static object DeepCopyObject(object obj)
{
BinaryFormatter Formatter = new BinaryFormatter(null,
new StreamingContext(StreamingContextStates.Clone));
MemoryStream stream = new MemoryStream();
Formatter.Serialize(stream, obj);
stream.Position = 0;
object clonedObj = Formatter.Deserialize(stream);
stream.Close();
return clonedObj;
}
五个场景的测试结果为:
I、触发异常SerializationException,原因是该类不支持序列化
“System.Runtime.Serialization.SerializationException”类型的第一次机会异常在 mscorlib.dll 中发生
System.Runtime.Serialization.SerializationException: 程序集“DataCopyTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null”中的类型“DataCopyTest.TestClass”未标记为可序列化。
在 System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType type)
在 System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type type, StreamingContext context)
在 System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo()
在 System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)
在 System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, S“DataCopyTest.vshost.exe”(托管(v4.0.30319)): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Numerics\v4.0_4.0.0.0__b77a5c561934e089\System.Numerics.dll”
erializationBinder binder)
在 System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck)
在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck)
在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph)
在 DataCopyTest.DataManHelper.DeepCopyObject(Object obj) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\DataManHelper.cs:行号 24
在 DataCopyTest.FormMain.FormMain_Load(Object sender, EventArgs e) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\FormMain.cs:行号 37
II、可正常复制 (√)
III、可正常复制 (√)
IV、触发异常SerializationException,原因是该类不支持序列化
“System.Runtime.Serialization.SerializationException”类型的第一次机会异常在 mscorlib.dll 中发生
System.Runtime.Serialization.SerializationException: 程序集“System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089”中的类型“System.Windows.Forms.TextBox”未标记为可序列化。
在 System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType type)
在 System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type type, StreamingContext context)
在 System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo()
在 System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)
在 System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)
在 System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck)
在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck)
在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph)
在 DataCopyTest.DataManHelper.DeepCopyObject(Object obj) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\DataManHelper.cs:行号 24
在 DataCopyTest.FormMain.FormMain_Load(Object sender, EventArgs e) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\FormMain.cs:行号 128
V、触发异常SerializationException,原因是该类不支持序列化
“System.Runtime.Serialization.SerializationException”类型的第一次机会异常在 mscorlib.dll 中发生
System.Runtime.Serialization.SerializationException: 程序集“System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089”中的类型“System.Windows.Forms.DataGridView”未标记为可序列化。
在 System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType type)
在 System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type type, StreamingContext context)
在 System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo()
在 System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)
在 System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)
在 System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck)
在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck)
在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph)
在 DataCopyTest.DataManHelper.DeepCopyObject(Object obj) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\DataManHelper.cs:行号 24
在 DataCopyTest.FormMain.FormMain_Load(Object sender, EventArgs e) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\FormMain.cs:行号 141
结论:利用序列化与反序列化到二进制流的方法深复制对象,只有在该对象支持Serializable特性时才可使用
测试深复制方法2
public static object DeepCopyObject(object obj)
{
Type t = obj.GetType();
PropertyInfo[] properties = t.GetProperties();
Object p = t.InvokeMember("", System.Reflection.BindingFlags.CreateInstance, null, obj, null);
foreach (PropertyInfo pi in properties)
{
if (pi.CanWrite)
{
object value = pi.GetValue(obj, null);
pi.SetValue(p, value, null);
}
}
return p;
}
五个场景的测试结果为:
I、不会触发异常,但结果完全错误
II、不会触发异常,但结果完全错误
III、不会触发异常,但结果完全错误
IV、Text字段赋值结果正确,但其他内容不能保证
V、触发异常ArgumentOutOfRangeException、TargetInvocationException
“System.ArgumentOutOfRangeException”类型的第一次机会异常在 System.Windows.Forms.dll 中发生
“System.Reflection.TargetInvocationException”类型的第一次机会异常在 mscorlib.dll 中发生
System.Reflection.TargetInvocationException: 调用的目标发生了异常。 ---> System.ArgumentOutOfRangeException: 指定的参数已超出有效值的范围。
参数名: value
在 System.Windows.Forms.DataGridView.set_FirstDisplayedScrollingColumnIndex(Int32 value)
--- 内部异常堆栈跟踪的结尾 ---
在 System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
在 System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
在 System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
在 System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)
在 System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, Object[] index)
在 DataCopyTest.DataManHelper.DeepCopyObject(Object obj) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\DataManHelper.cs:行号 29
在 DataCopyTest.FormMain.FormMain_Load(Object sender, EventArgs e) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\FormMain.cs:行号 141
结论:使用这种方法进行所谓深复制,完全是自寻死路!
测试深复制方法3
public static object DeepCopyObject(object obj)
{
if (obj != null)
{
object result = Activator.CreateInstance(obj.GetType());
foreach (FieldInfo field in obj.GetType().GetFields())
{
if (field.FieldType.GetInterface("IList", false) == null)
{
field.SetValue(result, field.GetValue(obj));
}
else
{
IList listObject = (IList)field.GetValue(result);
if (listObject != null)
{
foreach (object item in ((IList)field.GetValue(obj)))
{
listObject.Add(DeepCopyObject(item));
}
}
}
}
return result;
}
else
{
return null;
}
}
五个场景的测试结果为:
I、可正常复制(√)
II、可正常复制(√)
III、未触发异常, 复制后DataTable无行列
IV、未触发异常,Text字段未赋值
V、未触发异常
结论:这个方法只适用于深复制具备简单结构的类(如类中只有基础字段、数组等),对于不支持序列化的对象也可以进行深复制。
测试深复制方法4
这段代码来源同方法3
public static object DeepCopyObject(object obj)
{
if (obj == null)
return null;
Type type = obj.GetType();
if (type.IsValueType || type == typeof(string))
{
return obj;
}
else if (type.IsArray)
{
Type elementType = Type.GetType(
type.FullName.Replace("[]", string.Empty));
var array = obj as Array;
Array copied = Array.CreateInstance(elementType, array.Length);
for (int i = 0; i < array.Length; i++)
{
copied.SetValue(DeepCopyObject(array.GetValue(i)), i);
}
return Convert.ChangeType(copied, obj.GetType());
}
else if (type.IsClass)
{
object toret = Activator.CreateInstance(obj.GetType());
FieldInfo[] fields = type.GetFields(BindingFlags.Public |
BindingFlags.NonPublic | BindingFlags.Instance);
foreach (FieldInfo field in fields)
{
object fieldValue = field.GetValue(obj);
if (fieldValue == null)
continue;
field.SetValue(toret, DeepCopyObject(fieldValue));
}
return toret;
}
else
throw new ArgumentException("Unknown type");
}
五个场景的测试结果为:
I、可正常复制(√)
II、可正常复制(√)
III、触发异常MissingMethodException
“System.MissingMethodException”类型的第一次机会异常在 mscorlib.dll 中发生
System.MissingMethodException: 没有为该对象定义无参数的构造函数。
在 System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
在 System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
在 System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
在 System.Activator.CreateInstance(Type type, Boolean nonPublic)
在 System.Activator.CreateInstance(Type type)
在 DataCopyTest.DataManHelper.DeepCopyObject(Object obj) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\DataManHelper.cs:行号 45
在 DataCopyTest.DataManHelper.DeepCopyObject(Object obj) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\DataManHelper.cs:行号 53
在 DataCopyTest.FormMain.FormMain_Load(Object sender, EventArgs e) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\FormMain.cs:行号 99
IV、未触发异常,但Text字段也未赋值成功
V、触发异常MissingMethodException
“System.MissingMethodException”类型的第一次机会异常在 mscorlib.dll 中发生
System.MissingMethodException: 没有为该对象定义无参数的构造函数。
在 System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
在 System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
在 System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
在 System.Activator.CreateInstance(Type type, Boolean nonPublic)
在 System.Activator.CreateInstance(Type type)
在 DataCopyTest.DataManHelper.DeepCopyObject(Object obj) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\DataManHelper.cs:行号 45
在 DataCopyTest.DataManHelper.DeepCopyObject(Object obj) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\DataManHelper.cs:行号 53
在 DataCopyTest.FormMain.FormMain_Load(Object sender, EventArgs e) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\FormMain.cs:行号 141
结论:这个方法的作用类似方法3,只能深复制基本数据类型组成的类
具体问题具体分析
从上面的例子可以看出,想找一个放之四海而皆准的方式去深复制所有对象是很困难的。一些使用高级语言特性(反射)的深复制方法,即使可以在部分类上试用成功,也无法对所有的类都具备十足的把握。因此我认为应该采取下面的方式处理对象的深复制问题:
1、对于由基本数据类型组成的类,为之打上Serializable标签,直接使用序列化与反序列化的方法进行深复制
2、其他较为复杂的类型如DataGridView,可根据自身情况写一个方法进行深复制,之所以在这里说要根据自身情况写方法,是因为在对很多类进行复制时,你只需要复制对你有用的属性就行了。如TextBox控件中,只有Text一个属性对你是有用的,如果你需要在复制后的对象中用到Readonly等属性的值,那么在你自己实现的复制方法中,也加上对这些属性的赋值即可。这样做还有一个好处,就是方便进行一些定制化的开发。
如下面这段代码,就是对DataGridView的一个近似的深复制,这段代码将一个DataGridView(dgv)的内容复制到另一个DataGridView(dgvTmp)中,然后将dgvTmp传递给相关函数用于将DataGridView中的内容输出到Excel文档:
DataGridView dgvTmp = new DataGridView();
dgvTmp.AllowUserToAddRows = false; //不允许用户生成行,否则导出后会多出最后一行
for (int i = 0; i < dgv.Columns.Count; i++)
{
dgvTmp.Columns.Add(dgv.Columns[i].Name, dgv.Columns[i].HeaderText);
if (dgv.Columns[i].DefaultCellStyle.Format.Contains("N")) //使导出Excel文档金额列可做SUM运算
{
dgvTmp.Columns[i].DefaultCellStyle.Format = dgv.Columns[i].DefaultCellStyle.Format;
}
if (!dgv.Columns[i].Visible)
{
dgvTmp.Columns[i].Visible = false;
}
}
for (int i = 0; i < dgv.Rows.Count; i++)
{
object[] objList = new object[dgv.Rows[i].Cells.Count];
for (int j = 0; j < objList.Length; j++)
{
if (dgvTmp.Columns[j].DefaultCellStyle.Format.Contains("N"))
{
objList[j] = dgv.Rows[i].Cells[j].Value; //使导出Excel文档金额列可做SUM运算
}
else
{
objList[j] = dgv.Rows[i].Cells[j].EditedFormattedValue; //数据字典按显示文字导出
}
}
dgvTmp.Rows.Add(objList);
}
这段代码的特点如下:
1、DataGridView的属性AllowUserToAddRows要设置成false,否则导出到Excel文档后,会发现最后会多出一个空行。
2、我们在这里标记了那些列是隐藏列,这样在后面的处理中,如果要删除这些列,那删除的也是dgvTmp的列而不是dgv的列,保护了原数据。
3、对于部分数据字典的翻译,我们传的不是Value而是EditedFormattedValue,这种方式直接使用了dgv在屏幕上显示的翻译后文字,而不是原来的数据字典值。
4、对于部分金额列,需要直接传Value值,同时需要设置该列的DefaultCellStyle.Format,这样可使得这些内容在之后输出到Excel文档后,可做求和运算(Excel中类似“12,345.67”字符串是不能做求和运算的)。


猜你喜欢
- 前两天朋友问我,有没有使用过StackExchange.Redis,问我要个封装类,由于之前都是使用ServiceStack.Redis,由
- SpringMVC AbstractAnnotationConfigDispatcherSerServlet3.0环境中,容器会在类路径中查
- 这篇文章主要介绍了线程池中使用spring aop事务增强,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要
- 打开ITerm终端进入命令输入,sudo su,输入密码创建.bash_profile文件touch .bash_profile打开.bas
- 1. dip: device independent pixels(设备独立像素). 不同设备有不同的显示效果,这个和设备硬件有关,一般我们
- 1. 引入jar包pom.xml文件<?xml version="1.0" encoding="UTF-
- MongoDB的基本使用添加依赖<dependency>
- 本文通过C#程序代码展示如何给PDF文档添加可视化数字签名和不可见数字签名。可视化数字签名,即在PDF文档中的指定页面位置添加签名,包含相关
- C# DateTime与时间戳的相互转换,包括JavaScript时间戳和Unix的时间戳。1. 什么是时间戳首先要清楚JavaScript
- 在处理大文件时,如果利用普通的FileInputStream 或者FileOutputStream 抑或RandomAccessFile 来
- < drawable name="white">#FFFFFF< /drawable><
- 1.前面讲解的都是通过直接读取配置文件,进行的实例化ApplicationContextAbstractApplicationContext
- 题主要区分清楚内码(internal encoding)和外码(external encoding)就好了。内码是程序内部使用的字符编码,特
- 之前写轮播条或者指示器的时候都是UI图里面直接有,这样的效果并不好,给用户的体验比较差,所以闲暇之余自己写了个指示器,可以展现出一个优雅的效
- AudioSource 组件参考属性属性说明Clip音频资源Volume音量大小Mute是否静音Loop是否循环Play on load加载
- logback过滤部分日志输出场景使用监控异常日志进行告警时,部分异常日志可能只是不需要告警,但无法通过编码去除时,可以通过不输出这类异常日
- 题目描述Java创建线程的几种方式Java使用Thread类代表线程,所有线程对象都必须是Thread类或者其子类的实例。Java可以用以下
- Parallel类是对线程的一个抽象。该类位于System.Threading.Tasks名称空间中,提供了数据和任务并行性。Paralle
- Android屏蔽软键盘并且显示光标的实例详解如果是android4.0以下,那么editText.setInputType(InputTy
- maven配置项目的jdk版本无效排查最近在配置项目的jdk的时候发现在pom.xml中配置的1.8版本无效,maven更新后就变成了1.7