以OLE方式讀寫EXCEL的C++類【轉載】
◆C++中的【L"https://msdn.microsoft.com/library"】。
這個是C++標準規定的寫法。詳見以下連結:
◆VC中的 _T 和 _TEXT 和 TEXT 巨集。
_T和_TEXT和TEXT是通用的文字巨集,它們在被使用的時候,需要和字串文字/字元常量在一起才能使用。比如_T("https://msdn.microsoft.com/library")。
它們在和符號常量一起使用的時候,還可能將符號常量原封不動的解析成符號常量自己。比如_T('x')解析成char的'x'而不是wchar_t的'x'。
◆ VC 中的 LPSTR 和 LPCSTR 和 LPWSTR 和 LPCWSTR 和 LPTSTR 和 LPCTSTR 巨集。
LPSTR: (實際上是char*)。 是一個32位的指標,這個指標指向一個字串,字串中的每一個字元佔一個位元組。
LPWSTR: (實際上是wchar*)。是一個32位的指標,這個指標指向一個字串,字串中的每一個字元佔一個位元組或多個位元組。
LPCSTR: (實際上是const char*)。 是一個32位的指標,這個指標指向一個常量字串,字串中的每一個字元佔一個位元組。
LPCWSTR:(實際上是const wchar*)。是一個32位的指標,這個指標指向一個常量字串,字串中的每一個字元佔一個位元組或多個位元組。
LPTSTR: 根據MBCS巨集還是Unicode巨集,LPTSTR會轉換為LPSTR 或LPWSTR。 類似於_T巨集。
LPCTSTR:根據MBCS巨集還是Unicode巨集,LPTSTR會轉換為LPCSTR或LPCWSTR。類似於_T巨集。
◆以OLE方式讀寫EXCEL的C++類。
標頭檔案:illusion_excel_file.h
cpp檔案:illusion_excel_file.cpp#ifndef ILLUSION_EXCEL_FILE_H_H #define ILLUSION_EXCEL_FILE_H_H /****************************************************************************************** 此檔案copy自http://blog.csdn.net/fullsail/article/details/8449448 對於程式碼,我僅僅修正了編譯錯誤,對其他程式碼基本沒有更改。時間:2016-03-20。 ******************************************************************************************/ //OLE的標頭檔案 #include "CApplication.h" #include "CWorkbooks.h" #include "CWorkbook.h" #include "CWorksheets.h" #include "CWorksheet.h" #include "CRange.h" /// ///用於OLE的方式的EXCEL讀寫, class IllusionExcelFile { public: //建構函式和解構函式 IllusionExcelFile(); virtual ~IllusionExcelFile(); public: /// void ShowInExcel(BOOL bShow); ///檢查一個CELL是否是字串 BOOL IsCellString(long iRow, long iColumn); ///檢查一個CELL是否是數值 BOOL IsCellInt(long iRow, long iColumn); ///得到一個CELL的String CString GetCellString(long iRow, long iColumn); ///得到整數 int GetCellInt(long iRow, long iColumn); ///得到double的資料 double GetCellDouble(long iRow, long iColumn); ///取得行的總數 int GetRowCount(); ///取得列的總數 int GetColumnCount(); ///使用某個sheet, BOOL LoadSheet(long table_index, BOOL pre_load = FALSE); ///通過名稱使用某個sheet, BOOL LoadSheet(LPCTSTR sheet, BOOL pre_load = FALSE); ///通過序號取得某個Sheet的名稱 CString GetSheetName(long table_index); ///得到Sheet的總數 int GetSheetCount(); ///開啟檔案 BOOL OpenExcelFile(LPCTSTR file_name); ///關閉開啟的Excel 檔案,有時候開啟EXCEL檔案就要 void CloseExcelFile(BOOL if_save = FALSE); //另存為一個EXCEL檔案 void SaveasXSLFile(const CString &xls_file); ///取得開啟檔案的名稱 CString GetOpenFileName(); ///取得開啟sheet的名稱 CString GetLoadSheetName(); ///寫入一個CELL一個int void SetCellInt(long irow, long icolumn, int new_int); ///寫入一個CELL一個string void SetCellString(long irow, long icolumn, CString new_string); public: ///初始化EXCEL OLE static BOOL InitExcel(); ///釋放EXCEL的 OLE static void ReleaseExcel(); ///取得列的名稱,比如27->AA static char *GetColumnName(long iColumn); protected: ///開啟的EXCEL檔名稱 CString open_excel_file_; ///EXCEL BOOK集合,(多個檔案時) CWorkbooks excel_books_; ///當前使用的BOOK,當前處理的檔案 CWorkbook excel_work_book_; ///EXCEL的sheets集合 CWorksheets excel_sheets_; ///當前使用sheet CWorksheet excel_work_sheet_; ///當前的操作區域 CRange excel_current_range_; ///是否已經預載入了某個sheet的資料 BOOL already_preload_; ///Create the SAFEARRAY from the VARIANT ret. COleSafeArray ole_safe_array_; protected: ///EXCEL的程序例項 static CApplication excel_application_; protected: //預先載入 void PreLoadSheet(); }; #endif//ILLUSION_EXCEL_FILE_H_H
/******************************************************************************************
Copyright : 2000-2004, Appache 2.0
FileName : illusion_excel_file.cpp
Author : Sail
Version :
Date Of Creation : 2009年4月3日
Description :
Others :
Function List :
1. ......
Modification History:
1.Date :
Author :
Modification :
這個類是從網上下載的,我坐享其成,感謝原來的作者,我只試試是稍稍做了一下修正。
修正包括一些引數的使用不謹慎,bool 改為BOOL等,對於物件關係,我改了一部分,感覺原來的作者對於OO的思路部分不是很清楚。
對於這類東西OLE,我完全不瞭解,用別人封裝的東西感覺還是放心了很多,C++,偉大的C++
http://blog.csdn.net/gyssoft/archive/2007/04/29/1592104.aspx
OLE讀寫EXCEL都比較慢,所以應該儘量減少OLE的次數
對於讀取,還有解決方法,請試用一下預載入的方式,這個方法一次載入所有的讀取資料,如此速度就飛快了。
據說寫資料是沒有什麼方法加快的
http://topic.csdn.net/t/20030626/21/1962211.html
增加了一些寫入方式的程式碼,保證可以寫入EXCEL資料區,但是對於儲存,我發現如果呼叫CLOSE並且儲存的方式,
速度非常慢,我不理解為什麼。
所以我吧EXCEL打開了,讓你進行後續管理,
******************************************************************************************/
/******************************************************************************************
此檔案copy自http://blog.csdn.net/fullsail/article/details/8449448,我保留了檔案頭部的原始的註釋。
對於程式碼,我僅僅修正了編譯錯誤,對其他程式碼基本沒有更改。時間:2016-03-20。
******************************************************************************************/
//-----------------------excelfile.cpp----------------
#include "StdAfx.h"
#include "illusion_excel_file.h"
//定義了3個全域性物件
COleVariant
covTrue((short)TRUE),
covFalse((short)FALSE),
covOptional((long)DISP_E_PARAMNOTFOUND, VT_ERROR);
//定義了IllusionExcelFile類的靜態資料成員。
CApplication IllusionExcelFile::excel_application_;
IllusionExcelFile::IllusionExcelFile() :
already_preload_(FALSE)
{
}
IllusionExcelFile::~IllusionExcelFile()
{
//
CloseExcelFile();
}
//初始化EXCEL檔案,
BOOL IllusionExcelFile::InitExcel()
{
CoInitializeEx(NULL, COINIT_MULTITHREADED);//https://msdn.microsoft.com/en-us/library/ms886306.aspx
//建立Excel伺服器(啟動Excel)
if (!excel_application_.CreateDispatch(_T("Excel.Application"), NULL))
{
AfxMessageBox(_T("建立Excel服務失敗, 你可能沒有安裝EXCEL, 請檢查!"));
return FALSE;
}
excel_application_.put_DisplayAlerts(FALSE);
return TRUE;
}
//
void IllusionExcelFile::ReleaseExcel()
{
excel_application_.Quit();
excel_application_.ReleaseDispatch();
excel_application_ = NULL;
}
//開啟excel檔案
BOOL IllusionExcelFile::OpenExcelFile(LPCTSTR file_name)
{
//先關閉
CloseExcelFile();
//利用模板檔案建立新文件
excel_books_.AttachDispatch(excel_application_.get_Workbooks(), true);
LPDISPATCH lpDis = NULL;
lpDis = excel_books_.Add(COleVariant(file_name));
if (lpDis)
{
excel_work_book_.AttachDispatch(lpDis);
//得到Worksheets
excel_sheets_.AttachDispatch(excel_work_book_.get_Worksheets(), true);
//記錄開啟的檔名稱
open_excel_file_ = file_name;
return TRUE;
}
return FALSE;
}
//關閉開啟的Excel 檔案,預設情況不儲存檔案
void IllusionExcelFile::CloseExcelFile(BOOL if_save)
{
//如果已經開啟,關閉檔案
if (open_excel_file_.IsEmpty() == FALSE)
{
//如果儲存,交給使用者控制,讓使用者自己存,如果自己SAVE,會出現莫名的等待
if (if_save)
{
ShowInExcel(TRUE);
}
else
{
//
excel_work_book_.Close(COleVariant(short(FALSE)), COleVariant(open_excel_file_), covOptional);
excel_books_.Close();
}
//開啟檔案的名稱清空
open_excel_file_.Empty();
}
excel_sheets_.ReleaseDispatch();
excel_work_sheet_.ReleaseDispatch();
excel_current_range_.ReleaseDispatch();
excel_work_book_.ReleaseDispatch();
excel_books_.ReleaseDispatch();
}
void IllusionExcelFile::SaveasXSLFile(const CString &xls_file)
{
excel_work_book_.SaveAs(COleVariant(xls_file),
covOptional,
covOptional,
covOptional,
covOptional,
covOptional,
0,
covOptional,
covOptional,
covOptional,
covOptional,
covOptional);
return;
}
int IllusionExcelFile::GetSheetCount()
{
return excel_sheets_.get_Count();
}
CString IllusionExcelFile::GetSheetName(long table_index)
{
CWorksheet sheet;
sheet.AttachDispatch(excel_sheets_.get_Item(COleVariant((long)table_index)), true);
CString name = sheet.get_Name();
sheet.ReleaseDispatch();
return name;
}
//按照序號載入Sheet表格,可以提前載入所有的表格內部資料
BOOL IllusionExcelFile::LoadSheet(long table_index, BOOL pre_load)
{
LPDISPATCH lpDis = NULL;
excel_current_range_.ReleaseDispatch();
excel_work_sheet_.ReleaseDispatch();
lpDis = excel_sheets_.get_Item(COleVariant((long)table_index));
if (lpDis)
{
excel_work_sheet_.AttachDispatch(lpDis, true);
excel_current_range_.AttachDispatch(excel_work_sheet_.get_Cells(), true);
}
else
{
return FALSE;
}
already_preload_ = FALSE;
//如果進行預先載入
if (pre_load)
{
PreLoadSheet();
already_preload_ = TRUE;
}
return TRUE;
}
//按照名稱載入Sheet表格,可以提前載入所有的表格內部資料
BOOL IllusionExcelFile::LoadSheet(LPCTSTR sheet, BOOL pre_load)
{
LPDISPATCH lpDis = NULL;
excel_current_range_.ReleaseDispatch();
excel_work_sheet_.ReleaseDispatch();
lpDis = excel_sheets_.get_Item(COleVariant(sheet));
if (lpDis)
{
excel_work_sheet_.AttachDispatch(lpDis, true);
excel_current_range_.AttachDispatch(excel_work_sheet_.get_Cells(), true);
}
else
{
return FALSE;
}
//
already_preload_ = FALSE;
//如果進行預先載入
if (pre_load)
{
already_preload_ = TRUE;
PreLoadSheet();
}
return TRUE;
}
//得到列的總數
int IllusionExcelFile::GetColumnCount()
{
CRange range;
CRange usedRange;
usedRange.AttachDispatch(excel_work_sheet_.get_UsedRange(), true);
range.AttachDispatch(usedRange.get_Columns(), true);
int count = range.get_Count();
usedRange.ReleaseDispatch();
range.ReleaseDispatch();
return count;
}
//得到行的總數
int IllusionExcelFile::GetRowCount()
{
CRange range;
CRange usedRange;
usedRange.AttachDispatch(excel_work_sheet_.get_UsedRange(), true);
range.AttachDispatch(usedRange.get_Rows(), true);
int count = range.get_Count();
usedRange.ReleaseDispatch();
range.ReleaseDispatch();
return count;
}
//檢查一個CELL是否是字串
BOOL IllusionExcelFile::IsCellString(long irow, long icolumn)
{
CRange range;
range.AttachDispatch(excel_current_range_.get_Item(COleVariant((long)irow), COleVariant((long)icolumn)).pdispVal, true);
COleVariant vResult = range.get_Value2();
//VT_BSTR標示字串
if (vResult.vt == VT_BSTR)
{
return TRUE;
}
return FALSE;
}
//檢查一個CELL是否是數值
BOOL IllusionExcelFile::IsCellInt(long irow, long icolumn)
{
CRange range;
range.AttachDispatch(excel_current_range_.get_Item(COleVariant((long)irow), COleVariant((long)icolumn)).pdispVal, true);
COleVariant vResult = range.get_Value2();
//好像一般都是VT_R8
if (vResult.vt == VT_INT || vResult.vt == VT_R8)
{
return TRUE;
}
return FALSE;
}
//
CString IllusionExcelFile::GetCellString(long irow, long icolumn)
{
COleVariant vResult;
CString str;
//字串
if (already_preload_ == FALSE)
{
CRange range;
range.AttachDispatch(excel_current_range_.get_Item(COleVariant((long)irow), COleVariant((long)icolumn)).pdispVal, true);
vResult = range.get_Value2();
range.ReleaseDispatch();
}
//如果資料依據預先載入了
else
{
long read_address[2];
VARIANT val;
read_address[0] = irow;
read_address[1] = icolumn;
ole_safe_array_.GetElement(read_address, &val);
vResult = val;
}
if (vResult.vt == VT_BSTR)
{
str = vResult.bstrVal;
}
//整數
else if (vResult.vt == VT_INT)
{
str.Format(L"%d", vResult.pintVal);
}
//8位元組的數字
else if (vResult.vt == VT_R8)
{
str.Format(L"%0.0f", vResult.dblVal);
}
//時間格式
else if (vResult.vt == VT_DATE)
{
SYSTEMTIME st;
VariantTimeToSystemTime(vResult.date, &st);
CTime tm(st);
str = tm.Format("%Y-%m-%d");
}
//單元格空的
else if (vResult.vt == VT_EMPTY)
{
str = "";
}
return str;
}
double IllusionExcelFile::GetCellDouble(long irow, long icolumn)
{
double rtn_value = 0;
COleVariant vresult;
//字串
if (already_preload_ == FALSE)
{
CRange range;
range.AttachDispatch(excel_current_range_.get_Item(COleVariant((long)irow), COleVariant((long)icolumn)).pdispVal, true);
vresult = range.get_Value2();
range.ReleaseDispatch();
}
//如果資料依據預先載入了
else
{
long read_address[2];
VARIANT val;
read_address[0] = irow;
read_address[1] = icolumn;
ole_safe_array_.GetElement(read_address, &val);
vresult = val;
}
if (vresult.vt == VT_R8)
{
rtn_value = vresult.dblVal;
}
return rtn_value;
}
//VT_R8
int IllusionExcelFile::GetCellInt(long irow, long icolumn)
{
int num;
COleVariant vresult;
if (already_preload_ == FALSE)
{
CRange range;
range.AttachDispatch(excel_current_range_.get_Item(COleVariant((long)irow), COleVariant((long)icolumn)).pdispVal, true);
vresult = range.get_Value2();
range.ReleaseDispatch();
}
else
{
long read_address[2];
VARIANT val;
read_address[0] = irow;
read_address[1] = icolumn;
ole_safe_array_.GetElement(read_address, &val);
vresult = val;
}
//
num = static_cast<int>(vresult.dblVal);
return num;
}
void IllusionExcelFile::SetCellString(long irow, long icolumn, CString new_string)
{
COleVariant new_value(new_string);
CRange start_range = excel_work_sheet_.get_Range(COleVariant(L"A1"), covOptional);
CRange write_range = start_range.get_Offset(COleVariant((long)irow - 1), COleVariant((long)icolumn - 1));
write_range.put_Value2(new_value);
start_range.ReleaseDispatch();
write_range.ReleaseDispatch();
}
void IllusionExcelFile::SetCellInt(long irow, long icolumn, int new_int)
{
COleVariant new_value((long)new_int);
CRange start_range = excel_work_sheet_.get_Range(COleVariant(L"A1"), covOptional);
CRange write_range = start_range.get_Offset(COleVariant((long)irow - 1), COleVariant((long)icolumn - 1));
write_range.put_Value2(new_value);
start_range.ReleaseDispatch();
write_range.ReleaseDispatch();
}
//
void IllusionExcelFile::ShowInExcel(BOOL bShow)
{
excel_application_.put_Visible(bShow);
excel_application_.put_UserControl(bShow);
}
//返回開啟的EXCEL檔名稱
CString IllusionExcelFile::GetOpenFileName()
{
return open_excel_file_;
}
//取得開啟sheet的名稱
CString IllusionExcelFile::GetLoadSheetName()
{
return excel_work_sheet_.get_Name();
}
//取得列的名稱,比如27->AA
char *IllusionExcelFile::GetColumnName(long icolumn)
{
static char column_name[64];
size_t str_len = 0;
while (icolumn > 0)
{
int num_data = icolumn % 26;
icolumn /= 26;
if (num_data == 0)
{
num_data = 26;
icolumn--;
}
column_name[str_len] = (char)((num_data - 1) + 'A');
str_len++;
}
column_name[str_len] = '\0';
//反轉
_strrev(column_name);
return column_name;
}
//預先載入
void IllusionExcelFile::PreLoadSheet()
{
CRange used_range;
used_range = excel_work_sheet_.get_UsedRange();
VARIANT ret_ary = used_range.get_Value2();
if (!(ret_ary.vt & VT_ARRAY))
{
return;
}
//
ole_safe_array_.Clear();
ole_safe_array_.Attach(ret_ary);
}
完。
◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆
相關推薦
以OLE方式讀寫EXCEL的C++類【轉載】
◆C++中的【L"https://msdn.microsoft.com/library"】。 這個是C++標準規定的寫法。詳見以下連結: ◆VC中的 _T 和 _TEXT 和 TEXT 巨集。 _T和_TEXT和TEXT是通用的文字巨集,它們在被使用的時候,需要和字串
VC用OLE方式讀寫Excel
2) 這時會出現一個“從型別庫新增類嚮導”對話方塊,選中“檔案”選項,然後選擇檔案位置:C:\Program Files\Microsoft Office\Office12\EXCEL.EXE 將介面列表中的_Application,_Workbook,_Worksheet,Range,Ranges,Work
c++ 以二進位制和以文字方式讀寫檔案的區別
在c++專案開發中,時常涉及到檔案讀寫操作。因此在這裡先簡單梳理和回顧一下文字模式和二進位制模式在進行檔案讀寫上的區別。 1.linux平臺下文字檔案和二進位制檔案的讀寫 在linux平臺下進行檔案讀寫時,文字模式和二進位制模式沒有區別。在檔案讀寫時,呼叫fopen,無論以文字模式還是二進位制
高通平臺讀寫nv總結【轉】
本文轉載自:https://blog.csdn.net/suofeng12345/article/details/52713993 一,引言 1. 什麼是NV &nbs
關於InnoDB的讀寫鎖類型以及加鎖方式
索引 不完全 b2c text 按順序 net c中 加鎖 並不是 (本文為了方便,英文關鍵詞都都采用小寫方式,相關知識點會簡單介紹,爭取做到可以獨立閱讀) 文章開始我會先介紹本文需要的知識點如下: innodb的聚簇索引(聚集索引)和非聚簇索引(二級索引、非聚集索引
ApolloStudio高手之路(6):用Python以極簡方式讀寫OPC DA、OPC UA資料並實現UI控制元件自動繫結重新整理顯示
OPC(OLE for Process Control, 用於過程控制的OLE)是一個工業標準,OPC是為了連線資料來源(OPC伺服器)和資料的使用者(OPC應用程式)之間的軟體介面標準。資料來源可以是PLC,DCS,條形碼讀取器等控制裝置。隨控制系統構成的不同,作為資料來源的OPC伺服器既可以
ApolloStudio高手之路(3):用Python以最優雅簡潔的方式讀寫支援Modbus RTU/TCP協議的裝置資料
縱觀整個工業界生態鏈,可能Modbus協議(包括了通過串列埠方式連線的Modbus RTU協議和通過網路傳輸的Modbus TCP協議,這裡統稱為Modbus)作為一種工業領域通訊協議的業界標準在所有裝置互聯的協議群中獨佔鰲頭,也許正是因為其相較與其他協議的優越性(1.公開發表並且無版權要求;2
JavaSE8基礎 BufferedReader char[]方式讀寫實現復制粘貼txt文件
2-2 jdk stat blog pac ring str read 相對 os :windows7 x64 jdk:jdk-8u131-windows-x64 ide:Eclipse Oxygen Release (4.7.0) in
properties文件讀寫工具類PropertiesUtil.java
prop ace sys pan str pri finally res println import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException
Spring-Boot ? ShapeFile文件讀寫工具類+接口調用
void while har ble mage cto next() bound 添加 一、項目目錄結構樹 二、項目啟動 三、往指定的shp文件裏寫內容 (1) json數據【Post】 {
【轉載】python基礎-檔案讀寫'r' 與 'rb' 和‘r+'與’rb+'區別
【轉載連結:https://www.cnblogs.com/nulige/p/6128948.html】 一、Python檔案讀寫的幾種模式: r,rb,w,wb 那麼在讀寫檔案時,有無b標識的的主要區別在哪裡呢? 1、檔案使用方式標識
大白話聊聊Java併發面試問題之微服務註冊中心的讀寫鎖優化【石杉的架構筆記】
歡迎關注個人公眾號:石杉的架構筆記(ID:shishan100) 週一至週五早8點半!精品技術文章準時送上! 一、讀寫鎖的介紹 上一篇文章大白話聊聊Java併發面試問題之公平鎖與非公平鎖是啥?,聊了一下java併發包的公平鎖和非公平鎖。 這篇文章來聊一下讀寫鎖。所謂的讀寫鎖,就是將一個鎖拆分為讀鎖和寫鎖兩
C語言二進位制方式讀寫檔案資料
fwrite(buffer, size, count,fp):以二進位制的形式向指定的檔案中寫入若干資料項(由count決定),返回實際寫入的資料項數目,各引數含義如下: buffer:一個儲存區的起始地址,以該地址開始的儲存區的資料即是儲存到檔案中的資料,可以是陣列或指標型別; siz
[C#原始碼]網路資料流讀寫封裝類,支援多執行緒下同時讀和寫,自動資源管理,字串分隔符\r\n
using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using Syst
利用反射給JavaBean中的屬性進行讀寫操作類PropertyDescriptor
概述 PropertyDescriptor描述Java Bean中通過一對儲存器方法(getter / setter)匯出的一個屬性。我們可以通過該PropertyDescriptor對bean中的該屬性進行讀取和寫入操作,也可以設定其getter / setter。
Qt---多種方式讀寫二進位制檔案
將文字資料寫入二進位制檔案,然後從二進位制檔案中讀出,還原為文字資料 #include "mainwindow.h" #include <QApplication> #include &l
C#共享記憶體兩個程序軟體互相讀寫實現類
我在網上找了很多原始碼,沒有一個可以用2個程式實現互相讀寫的共享記憶體功能,一般只能單向傳遞,沒有任何意義,於是我自己封裝了一個類,但是看起來沒任何問題,就是不能共享,現在我把程式碼貼出來,請大神幫忙看看 using System; using System.IO; using System
Excel讀寫工具類
緣起 在J2SE和J2EE應用程式開發中,經常會遇到上傳Excel,匯出Excel的功能開發,對Excel的操作無非就是讀取Excel檔案內容轉成javabean,或者是將javabean物件寫入Excel檔案中。為了方便對Excel進行讀寫操作,可以將這塊程
android 檔案讀寫工具類
public static String readFile(File file) throws IOException{FileInputStream inputStream = new FileInputStream(file);int len=inputStream.available();byte []
【轉載】ReentrantReadWriteLock讀寫鎖的使用
Lock比傳統執行緒模型中的synchronized方式更加面向物件,與生活中的鎖類似,鎖本身也應該是一個物件。兩個執行緒執行的程式碼片段要實現同步互斥的效果,它們必須用同一個Lock物件。 讀寫鎖:分為讀鎖和寫鎖,多個讀鎖不互斥,讀鎖與寫鎖互斥,這是由jvm自己控制的