开发实践中遇到的一些问题及答案(Android篇一)

Some problems encountered and their answers in work (Android Chapter)


文件分块的几种方法

方法一:
传输

1. 定义每块文件的大小,由此确定每块文件的字节数

2. 将文件转换成字节数组

3. 将字节数组按照每块的字节数进行分割,得到每块字节数组

4. 将得到的每块字节数组转发到接收方

5. 接收方接收每块字节数组,将每块字节数组组合起来,即可将原文件重建。
方法二:

样例

以下是使用Java实现文件分块的代码样例:

```java
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class FileSplitSample {

   /**
    * 将文件分割为指定大小的文件块
    * 
    * @param filePath 要分割的文件路径
    * @param blockSize 块的大小
    */
   public static void fileSplit(String filePath, int blockSize) {
      // 根据文件路径构建文件对象
      File file = new File(filePath);
      // 将文件对象转换为字节数组
      byte[] fileBytes = new byte[(int) file.length()];
      try (InputStream inputStream = new FileInputStream(file)) {
         inputStream.read(fileBytes);
      } catch (IOException e) {
         e.printStackTrace();
      }
      // 如果字节数组的长度大于块的大小,则对字节数组进行分割,每块大小为blockSize
      if (fileBytes.length > blockSize) {
         int blockCount = fileBytes.length / blockSize;
         int lastBlockSize = fileBytes.length % blockSize;
         // 先分析前blockCount - 1个数据块
         for (int i = 0; i < blockCount - 1; i++) {
            byte[] blockBytes = new byte[blockSize];
            System.arraycopy(fileBytes, i * blockSize, blockBytes, 0, blockSize);
            // 将块的字节数组转换为新的文件,文件名格式为“文件路径_块索引”
            File newFile = new File(filePath + "_" + i);
            // TODO:对blockBytes进行处理后存储到newFile
         }
         // 再处理最后一个数据块
         if (lastBlockSize > 0) {
            byte[] blockBytes = new byte[lastBlockSize];
            System.arraycopy(fileBytes, blockCount * blockSize, blockBytes, 0, lastBlockSize);
            // 将块的字节数组转换为新的文件,文件名格式为“文件路径_块索引”
            File newFile = new File(filePath + "_" + blockCount);
            // TODO:对blockBytes进行处理后存储到newFile
         }
      }
   }

   public static void main(String[] args) {
      String filePath = "data.txt";
      int blockSize = 10;
      fileSplit(filePath, blockSize);
   }
}
方法三:



import java.io.*;

public class demo{
	public static void main(String[] args) {
		// 文件路径
		String path = "e:/test.txt";
		// 块大小
		int size = 1000;
		byte[] b = getFileBytes(path, size);
		for(int i = 0; i < b.length; i++) {
			System.out.print(b[i] + " ");
		}
	}
	
	public static byte[] getFileBytes(String path, int size) {
		byte[] buffer = null;
		try {
			File file = new File(path);
			// 获取文件大小
			long len = file.length();
			// 将字节大小转为块数
			int num = (int)Math.ceil(len / (double)size);
			buffer = new byte[num * size];
			// 分块读取
			for(int i = 0; i < num; i++) {
				int start = i * size;
				int end = (i + 1) * size > len ? (int)len : (i + 1) * size;
				InputStream fis = new FileInputStream(file);			
				fis.read(buffer, start, end - start);
				fis.close();			
			}
		} catch(Exception e) {
			e.printStackTrace();
		}
		return buffer;
	}
}

方法四:


```java
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

public class BlockFile {

public boolean splitFile(String srcPath, long blockSize){
    File srcFile = new File(srcPath);
    if(srcFile.isFile() && srcFile.exists()){
        long fileLength = srcFile.length();

        try {
            // 将文件输入流映射到内存地址
            FileInputStream fis = new FileInputStream(srcFile);
            byte[] byteArray = new byte[(int)blockSize];
            int amount = 0;

            int i = 0;
            while((amount = fis.read(byteArray)) != -1){     
                //先读取一定量的数据,然后写入新的文件
                i++;
                // 生成新的文件;
                File newFile = new File(srcPath +"_" + i);
                FileOutputStream fos = new FileOutputStream(newFile);
                ByteBuffer bf = ByteBuffer.wrap(byteArray, 0, amount);       
                //则采用一个ByteBuffer数组来存储从输入流中读入的字节
                fos.write(bf.array());      
                //将ByteBuffer中所有的数据强制转换为字节数据,然后根据此字节数组构造一个输出流对象,再往里写入数据
                fos.flush();
                fos.close();
            }

            fis.close();
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }else{
        return false;
    }
}

public static void main(String[] args) {
    String srcPath = "E://daily.txt";      
    //待分割文件路径
    BlockFile bf = new BlockFile();
    bf.splitFile(srcPath, 2*1024); 
    // 以2K为一个单位进行分割
    System.out.println("文件分割完成!");
}
}
方法五:
程序

代码如下:

import java.io.*;

public class SplitFile {
    // 文件分块,count指定分块数量
    public static void split(String filePath, int count) {
        File file = new File(filePath);
        // 获取文件名
        String fileName = file.getName();
        // 获取文件大小
        long fileSize = file.length();
        // 计算每一块的大小
        long size = fileSize / count;
        // 计算文件的剩余大小
        long leftSize = fileSize;
        // 获取文件的输入流
        FileInputStream fileInputStream = null;
        try {
            fileInputStream = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return;
        }

// 循环指定数,分块
        for(int i = 0; i < count; i++) {
            if (i == count - 1) {
                size = leftSize;
            }
            // 通过循环,指定每一块的大小
            byte[] bytes = new byte[(int) size];
            int a = -1;
            try {
                a = fileInputStream.read(bytes);
            } catch (IOException e) {
                e.printStackTrace();
            }
            // 输出流,指定每一块的输出地址
            FileOutputStream fileOutputStream = null;
            try {
                fileOutputStream = new FileOutputStream(file.getParent() +"/"+ fileName.substring(0, fileName.lastIndexOf("."))+"_" + i + ".part");
                fileOutputStream.write(bytes);
                fileOutputStream.flush();
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            leftSize -= size;
        }
    }

    public static void main(String[] args) {
        String filePath = "D:/TestFile/a.mp4";
        split(filePath, 3);
    }
}
方法六:

用RandomAccessFile实现文件分块,并返回一个List集合。
	 * 
	 * @param filePath
	 *            文件路径
	 * @param blockSize
	 *            分块大小
	 * @return
	 */
	public static List<byte[]> blockSplit(String filePath, int blockSize) {
		List<byte[]> list = new ArrayList<byte[]>();
		try {
			RandomAccessFile rFile = new RandomAccessFile(filePath, "rw");
			long size = rFile.length();
			long blockCount = size % blockSize == 0 ? size / blockSize : size
					/ blockSize + 1;
			for (int i = 0; i < blockCount; i++) {
				byte[] buffer = new byte[blockSize];
				// 去掉最后一个block,后面的文件全部用0填充
				int readSize = (i != blockCount - 1) ? blockSize
						: (int) (size - i * blockSize);
				rFile.read(buffer, 0, readSize);
				list.add(buffer);
			}
			rFile.close();
			return list;
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		return list;
	}
}

方法七:


     *
     * @param file        文件
     * @param size        每块大小
     * @return
     * @throws Exception
     */
    public static List<File> getFilePartInfos(File file, long size) throws Exception {
        //总长度
        long length = file.length();
        //每块缓存时间
        long blockSize = size;
        //分块数
        int blockNum = (int) (length / size);
        if (length % size != 0) {
            blockNum++;
        }
        //存放切割后文件的文件夹
        String partPath = file.getAbsolutePath() + ".sp";
        File partPathFile = new File(partPath);
        //如果文件夹文件不存在,则创建
        if (!partPathFile.exists()) {
            partPathFile.mkdirs();
        }
        RandomAccessFile raf = new RandomAccessFile(file, "r");
        List<File> fileList = new ArrayList<File>();
        // 起始字节
        long startByte = 0;
        // 结束字节
        long endByte = 0;
        for (int i = 0; i < blockNum; i++) {
            startByte = i * blockSize;
            //最后一块
            if (i == blockNum - 1) {
                endByte = length;
            } else {
                endByte = (i + 1) * blockSize;
            }
            System.out.println("block:" + i + " startByte-> " + startByte + " endByte->" + endByte);
            fileList.add(cutFileBySize(file, startByte, endByte, partPathFile.getAbsolutePath(), i, raf));
        }
        raf.close();
        return fileList;
    }


    private static File cutFileBySize(File file, long startByte, long endByte, String partPath, int part, RandomAccessFile raf) throws IOException {
        //新文件名称
        File partFile = new File(partPath + "/" + file.getName() + "_" + part);
        RandomAccessFile raf2 = new RandomAccessFile(partFile, "rw");
        try {
            //Go
            raf.seek(startByte);
            //1.定义缓存数组
            byte[] flush = new byte[1024];
            int len = -1;
            //2.循环传输文件流
            while (startByte < endByte) {
                //把指针移动到当前索引为startByte
                raf.seek(startByte);
                if (endByte - startByte >= 1024) {
                    //从输入流中读取1024个字节写到缓存数组
                    len = raf.read(flush);
                } else {
                    //从输入流中读取剩余的字节写到缓存数组
                    len = raf.read(flush, 0, (int) (endByte - startByte));
                }
                if (len > 0) {
                    //从缓存数组中写到输出流
                    raf2.write(flush, 0, len);
                    startByte = raf.getFilePointer();
                } else {
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            raf2.close();
        }
        return partFile;
    }

    public void combinePartFile(List<File> files, String destFileName) {
        File preFile = null;
        try {
            File destFile = new File(destFileName);
            BufferedOutputStream destBufOutStream = new BufferedOutputStream(new FileOutputStream(destFile));
            byte[] flush = new byte[1024];
            int len = -1;
            for (File file : files) {
                if (file == null) {
                    throw new IllegalArgumentException("file should not be a null");
                }
                if(!file.exists()){
                    throw new RuntimeException(file.getAbsolutePath()+"文件不存在");
                }
                BufferedInputStream partBufInStream = new BufferedInputStream(new FileInputStream(file));

                if (preFile == null) {
                    while ((len = partBufInStream.read(flush)) != -1) {
                        destBufOutStream.write(flush, 0, len);
                    }
                } else {
                    long skipLen = destFile.length() - preFile.length();
                    while (skipLen > 0) {
                        long skip = partBufInStream.skip(skipLen);
                        skipLen -= skip;
                    }
                    while ((len = partBufInStream.read(flush)) != -1) {
                        destBufOutStream.write(flush, 0, len);
                    }
                }
                preFile = file;
                partBufInStream.close();
            }
            destBufOutStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
方法八:


import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class FileSplit {

    private static final int DEFAULT_BLOCK_SIZE=1024*1024;//默认每块1MB

    /**
     * @param file 需要分块的文件路径
     * @param size 每块的大小,单位Bytes
     * @param blockSuffix 每块文件的后缀名
     * @return List<File>
     */
    public static List<File> splitBySize(String file, int size,String blockSuffix){
        List<File> blocks=new ArrayList<File>();
        File f=new File(file);
        if(!f.exists()){
            System.out.println("指定的文件不存在。");
            return blocks;
        }else{
            //总的大小
            long length=f.length();
            System.out.println("OriginalFilelength="+length);
            //处理单块大小
            if(size<=0){
                size=DEFAULT_BLOCK_SIZE;
            }
            //计算块数
            int partNumber= (int) Math.ceil(1.0*length/size);
            System.out.println("Number of blocks is "+partNumber);
            int count=1;
            try {
                FileInputStream fis = new FileInputStream(f);
                //缓冲数组2k
                byte[] b = new byte[2048];
                for(int i=0;i<partNumber;i++){
                    File block=new File(f.getParent(),f.getName()+"_"+count+blockSuffix);;
                    //创建新文件
                    boolean createNewFile=block.createNewFile();
                    if(createNewFile){
                        blocks.add(block);
                        System.out.println("块"+count+"创建成功,path="+block.getAbsolutePath());
                    }
                    RandomAccessFile raf=new RandomAccessFile(block,"rw");
                    int readLength=0;
                    //未读完
                    while(readLength<size && (readLength=fis.read(b))>0){
                        raf.write(b,0,readLength);
                    }
                    raf.close();
                    count++;
                }
            }catch (IOException e){
                System.out.println("分块失败。");
                e.printStackTrace();
            }
        }
        return blocks;
    }

    public static void main(String[] args) {
        String FilePath="D:\\Project\\MyEclipseIDEworkspace\\page18.jpg";
        List<File> blocks = splitBySize(FilePath, 0, ".block");
        if(blocks!=null){
            blocks.forEach(block -> {
                System.out.println(block.getName()+"\tSize:"+block.length());
            });
        }
    }

}
方法九:



```java
import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class SplitFileUtil {
    /**
     * 指定大小分割文件
     * 根据指定大小,读取源文件,写入目标文件
     * @param src
     * @param blocksNumber
     * @return
     */
    public static List<File> splitFile(File src, int blocksNumber) throws IOException {
        List<File> result = new ArrayList<>();
        //文件总字节数
        long srcSize = src.length();
        //分块大小,每块最大字节数
        int blockSize = (int) Math.ceil(srcSize*1.0f/blocksNumber);
        //输入流
        FileInputStream fis = null;
        //输出流
        FileOutputStream fos = null;
        //缓冲
        byte[] buf = new byte[blockSize];
        try {
            fis = new FileInputStream(src);
            int len =0;
            while ((len=fis.read(buf))!=-1){
                File block = new File(src.getParent(), src.getName() + String.valueOf(result.size()));
                fos = new FileOutputStream(block);
                fos.write(buf,0,len);
                result.add(block);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            fis.close();
            fos.close();
        }
        return result;
    }

    public static void main(String[] args) throws IOException {
        File src = new File("beforeSplit.txt");
        List<File> splitFiles = splitFile(src, 3);
        for (int i=0;i<splitFiles.size();i++){
            System.out.println(splitFiles.get(i).getAbsoluteFile());
        }

    }

}

自定义EditText实现限制输入指定字符

要实现只允许输入指定字符的EditText,可以重写EditText的onTextChanged方法,并在其中过滤出指定字符,然后使用setSelection方法将光标定位到文本末尾。

以下是一个示例代码:

```
public class LimitedEditText extends EditText {

    private String mAllowedCharacters = "";
    private String mPreviousText = "";
    private int mPreviousLength = 0;

    public LimitedEditText(Context context) {
        super(context);
    }

    public LimitedEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public LimitedEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public void setAllowedCharacters(String allowedCharacters) {
        mAllowedCharacters = allowedCharacters;
    }

    @Override
    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
        super.onTextChanged(text, start, lengthBefore, lengthAfter);

        if (text.toString().equals(mPreviousText)) {
            // 已经处理过了,避免死循环
            return;
        }

        // 记录当前文本和长度
        String currentText = text.toString();
        int currentLength = currentText.length();

        // 检查每个字符是否允许输入
        StringBuilder filteredText = new StringBuilder();
        for (int i = 0; i < currentLength; i++) {
            String c = String.valueOf(currentText.charAt(i));
            if (mAllowedCharacters.contains(c)) {
                filteredText.append(c);
            }
        }

        // 更新文本和光标位置
        if (!currentText.equals(filteredText.toString())) {
            setText(filteredText.toString());
            setSelection(getText().length());
        }

        // 记录当前文本和长度
        mPreviousText = getText().toString();
        mPreviousLength = mPreviousText.length();
    }
}
```

使用时,可以通过setAllowedCharacters方法设置允许输入的字符,例如:

```
LimitedEditText editText = findViewById(R.id.editText1);
editText.setAllowedCharacters("ABC123");
```

这样,只有字符ABC123才能被输入。
public class MyEditText extends EditText {
    private String mAllowStr;

    public MyEditText(Context context) {
        super(context);
    }

    public MyEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public void setAllowStr(String allowStr) {
        mAllowStr = allowStr;
    }

    @Override
    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
        super.onTextChanged(text, start, lengthBefore, lengthAfter);
        if (TextUtils.isEmpty(mAllowStr)) {
            return;
        }
        if (text.toString().contains(mAllowStr)) {
            setText(mAllowStr);
            setSelection(mAllowStr.length());
        }
    }
}

获取Gps卫星数量

以下是一个获取GPS卫星数量的示例代码:

```
public class MainActivity extends AppCompatActivity {

    private TextView satelliteCountTextView;
    private LocationManager locationManager;
    private GpsStatus.Listener gpsStatusListener;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        satelliteCountTextView = findViewById(R.id.satellite_count_textview);

        locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        gpsStatusListener = new GpsStatus.Listener() {
            @Override
            public void onGpsStatusChanged(int event) {
                if (event == GpsStatus.GPS_EVENT_SATELLITE_STATUS) {
                    int count = 0;
                    Iterable<GpsSatellite> satellites = locationManager.getGpsStatus(null).getSatellites();
                    for (GpsSatellite satellite : satellites) {
                        if (satellite.usedInFix()) {
                            count++;
                        }
                    }
                    satelliteCountTextView.setText("GPS卫星数量:" + count);
                }
            }
        };
    }

    @Override
    protected void onResume() {
        super.onResume();
        locationManager.addGpsStatusListener(gpsStatusListener);
    }

    @Override
    protected void onPause() {
        super.onPause();
        locationManager.removeGpsStatusListener(gpsStatusListener);
    }
}
```

该示例代码包括以下步骤:

1. 创建一个 LocationManager 对象来管理位置信息。

2. 创建一个 GpsStatus.Listener 对象,当 GPS 状态发生变化时会调用该对象的 onGpsStatusChanged 方法。

3. 在 onResume 方法中注册 GpsStatus.Listener 对象,以便能够接收到 GPS 状态变化的通知。

4. 在 onPause 方法中取消注册 GpsStatus.Listener 对象,以避免在后台不必要的通知。

5. 在 onGpsStatusChanged 方法中获取 GPS 卫星数量。首先调用 LocationManager 对象的 getGpsStatus 方法来获取 GPS 状态信息,然后遍历所有的 GpsSatellite 对象,检查是否被用于位置定位并计算已用于定位的卫星数量。

6. 最后将数量显示到界面上。

请注意,您需要在 AndroidManifest.xml 文件中添加以下权限:

```
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

刷新RecycleView数据:

setNewData:
```
/**
 * Replaces the current data set with a new one.
 *
 * @param newData The new data set to set.
 */
public void setNewData(List<T> newData) {
    if (mData != null) {
        mData.clear();
        mData.addAll(newData);
    } else {
        mData = new ArrayList<>(newData);
    }
    notifyDataSetChanged();
}
```
clearSelections:
```
/**
 * Clears all selections in the adapter.
 */
public void clearSelections() {
    mSelectedItems.clear();
    notifyDataSetChanged();
}
```



setNewData方法实现:

public void setNewData(@Nullable List<T> data) {
    mData.clear();
    if (data != null) {
        mData.addAll(data);
    }
    notifyDataSetChanged();
}

clearSelections方法实现:

public void clearSelections() {
    for (int i = 0; i < mSelectedItems.size(); i++) {
        int position = mSelectedItems.keyAt(i);
        notifyItemChanged(position);
    }
    mSelectedItems.clear();
}



setNewData方法实现:

public void setNewData(List<T> newData) {
    mData = newData;
    notifyDataSetChanged(); // 通知数据已改变,刷新界面
}

clearSelections方法实现:

public void clearSelections() {
    for (int i = 0; i < mSelectedPositions.size(); i++) {
        int position = mSelectedPositions.keyAt(i);
        mSelectedPositions.put(position, false);
    }
    mSelectedPositions.clear();
    notifyDataSetChanged(); // 通知数据已改变,刷新界面
} 

其中mData为存放数据的列表,mSelectedPositions为记录选中状态的集合,key为位置,value为状态(true表示选中,false表示未选中)。同时,notifyDataSetChanged()方法是用来通知RecyclerView数据已改变,需要刷新数据显示的。


setNewData的实现:

首先判断传入的list是否为空,如果是则直接赋空值给mData。

如果不为空,则将传入的list赋值给mData,并且清空之前的选择状态及选择数据列表。

最后调用notifyDataSetChanged()方法通知适配器数据发生了改变。

```
public void setNewData(@Nullable List<T> list) {
    if (list == null) {
        mData = null;
    } else {
        mData = new ArrayList<>(list);
        mSelectedPositions.clear();
        mSelectedData.clear();
    }
    notifyDataSetChanged();
}
```

clearSelections的实现:

循环遍历选择状态的列表mSelectedPositions,将其中的每个位置对应的选择状态设置为false。

然后清空选择数据列表mSelectedData。

最后调用notifyDataSetChanged()方法通知适配器数据发生了改变。

```
public void clearSelections() {
    for (int i = 0; i < mSelectedPositions.size(); i++) {
        mSelectedPositions.set(i, false);
    }
    mSelectedData.clear();
    notifyDataSetChanged();
}
```

Android中的静态广播setPackage()方法里面传的参数是什么

用于指定静态广播接收者的包名。如果指定了包名,那么只有该包名的应用程序才能接收到该广播。下面是一个示例代码:
Intent intent = new Intent("com.example.MY_CUSTOM_ACTION");
intent.putExtra("message", "Hello World!");
intent.setPackage("com.example.myapp");
sendBroadcast(intent);
在上面的示例代码中,我们定义了一个自定义的广播动作  `com.example.MY_CUSTOM_ACTION` ,并向其添加了一个  
`message`  的字符串额外信息。调用  `setPackage()`  方法,将广播发送到包名为  `com.example.myapp`  
的应用程序中。这样,只有在包名为  `com.example.myapp`  的应用程序中注册了接收器的情况下,才会接收到该广播。
 需要注意的是,如果不指定包名,则所有应用程序都可以接收到该广播。另外,包名应该是目标应用程序的准确包名,
 并且应该具有正确的权限来接收广播。

Java多次调用单例模式类,类中的构造函数会重复执行吗:

Java 中,单例模式是一种创建只有一个对象的类设计模式。在多次调用单例模式类时,类中的构造函数只会执行一次。
 这是因为单例模式的本质是将构造函数私有化,使得外界无法通过构造函数来创建对象。相反,单例模式通过提供一个静态方法来获取单例对象。在该方法中,会先检查单例对象是否已经存在,如果不存在则创建一个新的对象,并保存到静态变量中,以便于下一次获取单例对象时直接返回。
 因此,多次调用单例模式类时,只会执行一次构造函数。下面是一个简单的单例模式示例代码:
public class Singleton {
    private static Singleton instance;
     private Singleton() {
        System.out.println("Singleton Constructor");
    }
     public static Singleton getInstance() {
        if (instance == null) {  // 如果单例对象不存在,则创建一个新的对象
            instance = new Singleton();
        }
        return instance;
    }
}
在上面的示例代码中,只有在单例对象不存在时才会创建一个新的对象,并输出一条日志。因此,无论调用多少次  `getInstance()`  方法,都只会输出一次日志。
 需要注意的是,单例模式在多线程环境下需要特别注意线程安全问题,可以使用双重检查锁定等方式来实现线程安全的单例模式。

单例双重检查锁定的实现方式:

在多线程环境下,为了确保只有一个对象被创建,需要使用双重检查锁定(Double-checked locking)的方式来实现线程安全的单例模式。
 双重检查锁定的实现方式包含以下几个步骤:
 1. 将构造函数私有化,以禁止外部直接创建对象。
2. 定义一个私有的静态变量,用于保存单例对象。
3. 提供一个公共的静态方法,用于获取单例对象。
4. 在公共方法中使用双重检查锁定的方式来确保线程安全。
 以下是一个使用双重检查锁定实现线程安全的单例模式示例代码:
public class Singleton {
     // 定义一个私有的静态变量用于保存单例对象
    private static volatile Singleton instance;
     // 将构造函数私有化,以禁止外部直接创建对象
    private Singleton() {}
     // 提供一个公共的静态方法,用于获取单例对象
    public static Singleton getInstance() {
        if (instance == null) {  // 第一次检查,如果单例对象未被创建,则进入同步代码块
            synchronized (Singleton.class) {
                if (instance == null) {  // 第二次检查,如果单例对象未被创建,则创建一个新的对象
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
在上面的示例代码中,使用了  `volatile`  关键字来确保多个线程都可以正确地访问  `instance`  变量。在  `getInstance()`  方法中,第一次检查  `instance`  是否已经被创建,如果未被创建,则进入同步代码块进行第二次检查。如果依然未被创建,则创建并返回单例对象。
 由于使用了双重检查锁定的方式,所以即使在多线程环境下也能正确地保证只有一个对象被创建。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值