由String看Java堆栈问题,包括==以及equal()。

Java中String与堆栈
本文探讨了Java中String对象的内存分配策略,特别是不同创建方式下(如直接赋值与new关键字)String实例在堆栈中的存储位置,并通过示例代码展示了==与equals()方法的区别。
由String看Java堆栈问题,包括==以及equal()。
首先看代码:
1

public class TestString {
public static void main(String[] args) {
String a0 = "abc";
String b0 = "abc";
if (a0 == b0) {
System.out.print("==");
} else {
System.out.print("!=");
}
}
}

执行结果为:
==

2

public class TestString {
public static void main(String[] args) {
String a0 = String.valueOf("abc");
String b0 = String.valueOf("abc");
if (a0 == b0) {
System.out.print("==");
} else {
System.out.print("!=");
}
}
}

执行结果为:
==

3

public class TestString {
public static void main(String[] args) {
String a0 = "abc"+"def";
String b0 = "abcdef";
if(a0==b0){
System.out.print("==");
}else{
System.out.print("!=");
}
}
}

执行结果为:
==

4

public class TestString {
public static void main(String[] args) {
String a0 = "abc";
String b0 = "def";
String c0 = "abcdef";
String d0 = a0 + b0;
if (c0 == d0) {
System.out.print("==");
} else {
System.out.print("!=");
}
}
}

执行结果为:
!=

5

public class TestString {
public static void main(String[] args) {
String a0 = new String("abc");
String b0 = new String("abc");
if (a0 == b0) {
System.out.print("==");
} else {
System.out.print("!=");
}
}
}

执行结果为:
!=

6

public class TestString {

public static void main(String[] args) {

String a0 = String.valueOf("abc")+String.valueOf("def");
String b0 = String.valueOf("abcdef");

if (a0 == b0) {
System.out.print("==");
} else {
System.out.print("!=");
}
}

}

执行结果为:
!=

我们的都知道,Java的内存分配策略:栈中存放基本数据类型(或者叫内置类型)以及引用类型(或者叫对象句柄),而堆中存放对象数据。
String很特殊,根据Think In Java介绍:“通过编译器和特殊的覆盖或过载运算符+和+=,可将引号字符串转换成一个String”。可见引号字符串本身并不是一个String,而是通过运算符的重载转换成了String。注意:没有=号。
故此:我们要问,引号字符串通过重载以后转换成String存在于什么位置?
查询网上没有具体的分析,书籍上也没有准确的说明。
根据执行结果猜想:
学习过类加载可能知道,有个常量区。
暂且假设将此部分数据存放于此空间中,看能否得到合理的解释。

Java的内存分配栈中存放两种类型数据:
1、基本数据类型:此处疑问?存放的是具体的数据还是也是一个内存地址。
假设1:栈中存放的是具体的数据,当然所有出现在基本类型的操作均可以解释。
假设2:数据存放于上边提到的常量区,而栈中存放的是此常量区对应数据的地址。所有基本数据类型操作似乎也可以解释。
其实Java设计师们很注重内存的利用,我们可能看见很多文章曾提到这样一个概念,当int n =1;时,Java会在内存中搜索,看有没有1的存在,假如有的话,则不会重新分配空间建立1,如果没有的话,则会建立1,假如int m=1;则Java不会再建立1的空间。可见似乎内存中存在一个存放数据的地方。如果用次观点解释假设2完全成立。
现在以下的结论均在假设2下展开。
2、引用类型:栈中存放的是对象的引用,此引用是通过new创建的对象在对中分配的地址。
现在分析String。
属于什么类型?基本数据类型、引用类型。
当作为String str = "abc";建立时,作为基本类型的假设2合理一些(参见第1部分代码)。
当作为String str = new String("abc");建立时,毫无疑问作为引用类型处理(参见第5部分代码)。

我们暂且将+作为一种Java特殊的处理机制,当它处理引号字符串时不会采用new方式建立String(即像"abc"+"def"),而对于栈中数据处理时会采用new方式建立String(即像a+b)。(参见第3、4、6部分代码)。

当作为String str = String.valueOf("abc");建立或者执行+时,完全可以解释通过。
其实String.valueOf()可以看做是对"abc"的封装,但不是new的(参见第2、6部分代码)。


其实现在要是按照以上的观点,可以得出这样一种结论:
栈中存放的是句柄,包括基本数据类型。内存中存在另外一块区域(常量区),存放基本数据类型数据以及引号字符串数据。
堆中存放的是对象,且必须是new的或者通过+运算符的重载隐式new的。

乱想了一通,与一些理论违背,因为好多东西解释不通,所以胡思乱想了。
欢迎评论,谢谢。
package com.isa.navi.library.map; import android.inputmethodservice.Keyboard; import android.os.Environment; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import androidx.annotation.NonNull; import com.isa.navi.library.constant.Const; import com.isa.navi.library.manager.BaseProxy; import com.isa.navi.library.map.DataCheckInfo; import com.isa.navi.library.map.UpdateEnvironment; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.CopyOnWriteArrayList; /** * @author haoj * @description description * @date 2023-06-20 */ public class MapProxy extends BaseProxy<IMapProxyAIDL> implements IMapProxy { private static final String TAG = Const.ISA_NAVI + "MapProxy"; private List<IMapListener.IMapChangeListener> mIUpdateCallbackListeners = new CopyOnWriteArrayList<>(); private IMapDownloadCallbackAIDL mIMapDownloadCallBackAIDL; private volatile static MapProxy mInstance; public static MapProxy getInstance() { if (mInstance == null) { synchronized (MapProxy.class) { if (mInstance == null) { mInstance = new MapProxy(); } } } return mInstance; } @Override protected String getProxyName() { return Const.SVR_MAP_REPOSITORY; } @Override protected IMapProxyAIDL getProxyService(IBinder iBinder) { return IMapProxyAIDL.Stub.asInterface(iBinder); } @Override public void updateMap() { try { if (isAvailable()) { mService.updateMap(); } } catch (RemoteException e) { e.printStackTrace(); } } @Override public void suspendUpdate(boolean suspend) { try { Log.d(TAG, "suspendUpdateMap suspend : " + suspend + ", isAvailable : " + isAvailable()); if (isAvailable()) { mService.suspendUpdate(suspend); } } catch (RemoteException e) { e.printStackTrace(); } } @Override public void cancelUpdateMap() { try { Log.d(TAG, "cancelUpdateMap : " + ", isAvailable : " + isAvailable()); if (isAvailable()) { mService.cancelUpdate(); } } catch (RemoteException e) { e.printStackTrace(); } } @Override public void addMapChangeListener(IMapListener.IMapChangeListener listener) { Log.d(TAG, "addDataChangeListener callback:" + listener + ", isAvailable : " + isAvailable()); try { if (isAvailable()) { if ((listener != null) && !mIUpdateCallbackListeners.contains(listener)) { mIUpdateCallbackListeners.add(listener); } if (mIUpdateCallbackListeners.size() == 1) { mIMapDownloadCallBackAIDL = new IMapDownloadCallbackAIDL.Stub() { @Override public void onMapDownloadStatusChange(List<UpdateInfoBean> bean) throws RemoteException { Log.d(TAG, "onMapDownloadStatusChange:" + bean + " ,size:" + mIUpdateCallbackListeners.size()); try { Iterator<IMapListener.IMapChangeListener> iterator = mIUpdateCallbackListeners.iterator(); while (iterator.hasNext()) { IMapListener.IMapChangeListener l = iterator.next(); l.onMapChange(bean); } } catch (Exception e) { e.printStackTrace(); } } }; mService.registerMapDownloadListener(mIMapDownloadCallBackAIDL); } } } catch (RemoteException e) { e.printStackTrace(); } } @Override public void removeMapChangeListener(IMapListener.IMapChangeListener listener) { Log.d(TAG, "removeDataChangeListener callback:" + listener + ", isAvailable : " + isAvailable()); try { if (isAvailable()) { if ((listener != null) && mIUpdateCallbackListeners.contains(listener)) { mIUpdateCallbackListeners.remove(listener); } if (mIUpdateCallbackListeners.size() == 0 && mIMapDownloadCallBackAIDL != null) { mService.unregisterMapDownloadListener(mIMapDownloadCallBackAIDL); } mIMapDownloadCallBackAIDL = null; } } catch (RemoteException e) { e.printStackTrace(); } } private static String calculateMD5(String filePath) { try { MessageDigest digest = MessageDigest.getInstance("MD5"); FileInputStream fis = new FileInputStream(filePath); byte[] buffer = new byte[8192]; int read = 0; while ((read = fis.read(buffer)) > 0) { digest.update(buffer, 0, read); } byte[] md5sum = digest.digest(); StringBuffer hexString = new StringBuffer(); for (int i = 0; i < md5sum.length; i++) { String hex = Integer.toHexString(0xff & md5sum[i]); if (hex.length() == 1) { hexString.append('0'); } hexString.append(hex); } return hexString.toString(); } catch (Exception e) { e.printStackTrace(); return ""; } } private Map<String, String> getDetectionInfo(String md5_file_path) throws JSONException { Map<String, String> dataMap = new HashMap<>(); boolean isUsbReady = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); if(!isUsbReady) { return dataMap; } JSONObject jsonObject = getJsonObject(md5_file_path); JSONArray jsonArray = jsonObject.getJSONArray("ISAData"); for(int i = 0; i< jsonArray.length(); i++) { JSONObject jsonObject1 = jsonArray.getJSONObject(i); String dataName = jsonObject1.getString("data_name"); String md5 = jsonObject1.getString("md5"); dataMap.put(dataName, md5); } return dataMap; } @NonNull private static JSONObject getJsonObject(String md5_file_path) throws JSONException { File file = new File(md5_file_path); StringBuilder content = new StringBuilder(); try{ BufferedReader reader = new BufferedReader(new FileReader(file)); String line; while ((line = reader.readLine()) != null) { content.append(line).append("\n"); } reader.close(); } catch (IOException e) { throw new RuntimeException(e); } JSONObject jsonObject = new JSONObject(content.toString()); return jsonObject; } public DataCheckInfo checkISAData(String md5_file_path) { DataCheckInfo info = new DataCheckInfo(); md5_file_path += "/ISADataCheckInfo.txt"; Log.d(TAG, "checkISAData md5_file_path = " + md5_file_path); //获取文件信息,用于校验数据 Map<String, String> md5_file_map = null; try { md5_file_map = getDetectionInfo(md5_file_path); } catch (JSONException e) { e.printStackTrace(); } //如果文件中的信息不足32个则返回报错信息 if(md5_file_map.isEmpty() || md5_file_map.size() != 32) { info.setIs_result(false); info.setFailure(2); info.setReasom_chinese("ISADataCheckInfo.txt文件信息缺失"); info.setReasom_engine("Insufficient information in ISADataCheckInfo.txt"); Log.d(TAG, "checkISAData md5_file_map 信息不足"); return info; } // public String get_Global_m_data_address() { // return mHmiProxyJNI.get_Global_m_data_address(); // } //在数据分区下,检索数据 String data_path = "/map/"; File directory = new File(data_path); Map<String, String> data_MD5_map = new HashMap<>(); if(directory.exists() && directory.isDirectory()) { File[] files = directory.listFiles(); if (files != null) { for(File file :files) { if(file.isFile()) { String file_name = file.getName(); if(file_name.indexOf("CarDBBIN") != -1 || file_name.indexOf("Country_CarDB") != -1) { String MD5 = ""; MD5 = calculateMD5(data_path+file_name); if(MD5 == "") { info.setIs_result(false); info.setFailure(4); info.setReasom_chinese("无法计算分区内数据MD5值"); info.setReasom_engine("Unable to calculate MD5 of data within the partition"); return info; } Log.d(TAG, "checkISAData file_name:" + file_name); Log.d(TAG, "checkISAData MD5:" + MD5); data_MD5_map.put(file_name, MD5); } } } } else { info.setIs_result(false); info.setFailure(3); info.setReasom_chinese("无法访问数据分区"); info.setReasom_engine("Unable to read data partition"); Log.d(TAG, "checkISAData 无法读取数据分区目录"); return info; } } else { info.setIs_result(false); info.setFailure(8); info.setReasom_chinese("数据分区不存在"); info.setReasom_engine("The data partition does not exist"); Log.d(TAG, "checkISAData 数据分区目录不存在"); return info; } Log.d(TAG, "data_MD5_map 组织完毕!"); //如果数据不足32个,则返回报错 if(data_MD5_map.size() != 32) { info.setIs_result(false); info.setFailure(5); info.setReasom_chinese("数据分区目录下数据缺失"); info.setReasom_engine("Data is missing in the data partition directory"); Log.d(TAG, "checkISAData 数据分区目录目录下数据不全"); return info; } //遍历data_MD5_map集合,跟校验信息进行判断 for(Map.Entry<String, String> entry: data_MD5_map.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); String md5_file_map_value = md5_file_map.get(key); if(md5_file_map_value != null) { Log.d(TAG, "value:" + value); Log.d(TAG, "md5_file_map_value:" + md5_file_map_value); if(!value.equals(md5_file_map_value)) { Log.d(TAG, "value size:" + value.length()); Log.d(TAG, "md5_file_map_value size:" + md5_file_map_value.length()); info.setIs_result(false); info.setFailure(6); info.setReasom_chinese(key + " 该国家数据校验不通过"); info.setReasom_engine(key + " verification failed"); Log.d(TAG, "checkISAData " + key + "该国家数据校验不通过"); } } else { info.setIs_result(false); info.setFailure(7); info.setReasom_chinese(key + " 无法获取分区内数据MD5值"); info.setReasom_engine(key + " MD5 value calculation failed"); Log.d(TAG, "checkISAData MD5值计算失败"); return info; } } info.setIs_result(true); return info; } public String getInfoAfterUpdates() { String result = ""; // try { // if (isAvailable()) { // result = mService.getInfoAfterUpdates(); // } // } catch (RemoteException e) { // e.printStackTrace(); // } return result; } public void startUSBDetection() { try { Log.d(TAG, "getVersion : " + ", isAvailable : " + isAvailable()); if (isAvailable()) { mService.startUSBDetection(); } } catch (RemoteException e) { e.printStackTrace(); } } @Override public String getMapInfo() { String result = ""; try { if (isAvailable()) { result = mService.getMapInfo(); } } catch (RemoteException e) { e.printStackTrace(); } return result; } @Override public String getUpdateVersion() { String result = ""; try { if (isAvailable()) { result = mService.getUpdateVersion(); } } catch (RemoteException e) { e.printStackTrace(); } return result; } @Override public UpdateEnvironment checkUpdateEnvironment() { UpdateEnvironment result = null; try { if (isAvailable()) { result = mService.checkUpdateEnvironment(); Log.i(TAG, "result.getResult()" + result.getResult()); Log.i(TAG, "result.getReason()" + result.getReason()); } } catch (RemoteException e) { e.printStackTrace(); } return result; } @Override public String getUpdatePurpose() { String result = ""; try { if (isAvailable()) { result = mService.getUpdatePurpose(); } } catch (RemoteException e) { e.printStackTrace(); } return result; } @Override public String getUpdateContent() { String result = ""; try { if (isAvailable()) { result = mService.getUpdateContent(); } } catch (RemoteException e) { e.printStackTrace(); } return result; } @Override public String getUserManual() { String result = ""; try { if (isAvailable()) { result = mService.getUserManual(); } } catch (RemoteException e) { e.printStackTrace(); } return result; } @Override public String getUpdatedContentDescription() { String result = ""; try { if (isAvailable()) { result = mService.getUpdatedContentDescription(); } } catch (RemoteException e) { e.printStackTrace(); } return result; } @Override public int getEstimatedTime() { int result = 0; try { if (isAvailable()) { result = mService.getEstimatedTime(); } } catch (RemoteException e) { e.printStackTrace(); } return result; } @Override public boolean validatePassword(String inputPassword) { try { if (isAvailable()) { return mService.validatePassword(inputPassword); } } catch (Exception e) { e.printStackTrace(); } return false; } } 我要在这个类导入package com.isa.navi.jni.hmi; import android.content.Context; import android.os.Message; import android.os.StatFs; import android.util.Log; import androidx.annotation.NonNull; import com.isa.navi.jni.hmi.bean.UpdateList; import com.isa.navi.jni.hmi.update.USBListener; import com.isa.navi.library.map.UpdateEnvironment; import com.isa.navi.library.map.UpdateInfoBean; import com.isa.navi.manager.CallBackManager; import com.isa.navi.manager.CarManager; import com.isa.navi.manager.base.BaseHandlerManager; import com.isa.navi.receiver.USBReceiver; import com.isa.navi.utils.GsonUtils; import com.isa.navi.utils.LogUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.File; import java.io.IOException; import java.time.LocalDate; import java.time.Month; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; public class HmiJNIImpl extends BaseHandlerManager implements IHmiJNI { private static final String TAG = "UPDATE"; private static final int MSG_DOWNLOAD_STATUS_CHANGE = 1; private final HmiProxyJNI mHmiProxyJNI; private final Format mFormat; private static final String PASSWORD_PREFIX = "DfCs"; private static final String PASSWORD_SUFFIX = "#@"; private static final int LAST_CHARS_LENGTH = 4; private static String updateUSBpath = ""; /** * 车辆管理实例,用于管理车辆相关的功能。 */ private CarManager mCarManager; private volatile static HmiJNIImpl mInstance; private boolean is_run = false; private final Set<String> USBPathSet = Collections.synchronizedSet(new HashSet<>()); public static HmiJNIImpl getInstance() { if (mInstance == null) { synchronized (HmiJNIImpl.class) { if (mInstance == null) { mInstance = new HmiJNIImpl(); } } } return mInstance; } private HmiJNIImpl() { super(true); mHmiProxyJNI = new HmiProxyJNI(); mFormat = Format.getInstance(); mCarManager = CarManager.getInstance(); } @Override public void initialize() { super.initialize(); LogUtils.d(TAG, "initialize : "); DfCert.getInstance().getService(); CallBackManager.getInstance().registerUSBEventChangeListener(new USBListener() { @Override public void path(String mountPath) { USBPathSet.add(mountPath); LogUtils.i(TAG,"USBPathSet:" + mountPath); } }); // 初始化C++模块 mHmiProxyJNI.InitializeCpp(); InstanceEngine(); } private int[] getCurrentYearAndQuarter() { int[] result = new int[2]; LocalDate currentDate = LocalDate.now(); // 获取当前年份 int currentYear = currentDate.getYear(); // 获取当前月份 Month currentMonth = currentDate.getMonth(); // 根据月份确定当前季度 int currentQuarter; switch (currentMonth) { case JANUARY: case FEBRUARY: case MARCH: currentQuarter = 1; break; case APRIL: case MAY: case JUNE: currentQuarter = 2; break; case JULY: case AUGUST: case SEPTEMBER: currentQuarter = 3; break; case OCTOBER: case NOVEMBER: case DECEMBER: currentQuarter = 4; break; default: currentQuarter = 0; } result[0] = currentYear; result[1] = currentQuarter; return result; } private int[] parseMapInfo(String mapInfo) { int[] result = new int[2]; // 检查输入字符串的长度是否为6(4位年份 + 2位季度) if (mapInfo == null || mapInfo.length() != 6) { LogUtils.e(TAG, "Input string must be in the format YYYYMM where YYYY is the year and MM is the quarter as a two-digit number."); return result; } // 提取年份(前4位) String yearStr = mapInfo.substring(0, 4); result[0] = Integer.parseInt(yearStr); // 提取季度(后2位),并验证它在1到4的范围内 String quarterStr = mapInfo.substring(4, 6); int quarter = Integer.parseInt(quarterStr); if (quarter < 1 || quarter > 4) { LogUtils.e(TAG, "Quarter must be between 1 and 4."); return result; } result[1] = quarter; return result; } public void dataVersionCheck() { Thread dataVersionCheckThread = new Thread(new Runnable() { @Override public void run() { try { UpdateInfoBean bean = new UpdateInfoBean(); String localVersion = mHmiProxyJNI.getMapInfo(); if(localVersion.isEmpty()) { bean.setUpdateStatus(4); handleDownloadStatusChange(bean); LogUtils.i(TAG, "Detected no updates for a long time"); return; } int[] currentYearAndQuarter = getCurrentYearAndQuarter(); int[] localYearAndQuarter = parseMapInfo(localVersion); int yearDifference = currentYearAndQuarter[0] - localYearAndQuarter[0]; int QuarterDifference = currentYearAndQuarter[1] - localYearAndQuarter[1]; if(yearDifference * 4 + QuarterDifference > 3) { bean.setUpdateStatus(4); handleDownloadStatusChange(bean); LogUtils.i(TAG, "Detected no updates for a long time"); } } catch (Exception e) { e.printStackTrace(); } } }); // 设置线程名称 dataVersionCheckThread.setName("ISA_NAVI_dataVersionCheck"); // 启动线程 dataVersionCheckThread.start(); } public void setContext(Context context) { if(mContext == null) { mContext = context; } language.getInstance().setContext(mContext); } @Override protected void handleMessage(Message msg) { switch (msg.what) { case MSG_DOWNLOAD_STATUS_CHANGE: handleDownloadStatusChange((String) msg.obj); break; default: break; } } private List<String> JsonArrayToListExample(JSONArray jsonArray) { List<String> stringList = new ArrayList<>(); try { // 遍历JSONArray for (int i = 0; i < jsonArray.length(); i++) { // 由于我们假设JSONArray只包含字符串,所以我们可以直接getString String item = jsonArray.getString(i); // 将字符串添加到列表中 stringList.add(item); } } catch (JSONException e) { e.printStackTrace(); } return stringList; } private UpdateInfoBean jsonToUpdateInfoBean(String json) { UpdateInfoBean result = null; try { // 解析整个JSON字符串为一个JSONObject JSONObject rootObject = new JSONObject(json); // 解析mUpdateMessage对象 JSONObject mUpdateMessage = rootObject.getJSONObject("mUpdateMessage"); String country = mUpdateMessage.getString("country"); double downloadSize = mUpdateMessage.getDouble("downloadSize"); boolean isCurrentCountry = mUpdateMessage.getBoolean("isCurrentCountry"); double progress = mUpdateMessage.getDouble("progress"); double totalSize = mUpdateMessage.getDouble("totalSize"); int updateStatus = mUpdateMessage.getInt("updateStatus"); String version = mUpdateMessage.getString("version"); // 解析mUpdateNotification对象 JSONObject mUpdateNotification = rootObject.getJSONObject("mUpdateNotification"); JSONArray updateException = mUpdateNotification.getJSONArray("UpdateException"); JSONArray updateFailedList = mUpdateNotification.getJSONArray("UpdateFailedList"); boolean updateResult = mUpdateNotification.getBoolean("UpdateResult"); JSONArray updateSuccessfulList = mUpdateNotification.getJSONArray("UpdateSuccessfulList"); // 解析mapStatus int mapStatus = rootObject.getInt("mapStatus"); UpdateInfoBean.UpdateNotification temp1 = new UpdateInfoBean.UpdateNotification(updateResult,JsonArrayToListExample(updateException),JsonArrayToListExample(updateSuccessfulList),JsonArrayToListExample(updateFailedList)); UpdateInfoBean.UpdateMessage temp2 = new UpdateInfoBean.UpdateMessage(country, version, totalSize, (int) progress, isCurrentCountry); temp2.setUpdateStatus(updateStatus); temp2.setDownloadSize(downloadSize); result = new UpdateInfoBean(mapStatus, temp1, temp2); } catch (Exception e) { LogUtils.e(TAG, "JSON parsing failed:" + e.toString()); } return result; } private void handleDownloadStatusChange(String json) { UpdateInfoBean bean = jsonToUpdateInfoBean(json); LogUtils.i(TAG, "handleDownloadStatusChange : " + bean); if (bean != null) { // 仅当status=8时检查is_run,其他状态直接放行 if(bean.getUpdateStatus() != 8 || is_run) { List<UpdateInfoBean> list = new ArrayList<>(); list.add(bean); CallBackManager.getInstance().notifyUpdateStatus(list); } } } private void handleDownloadStatusChange(UpdateInfoBean bean) { LogUtils.i(TAG, "handleDownloadStatusChange : " + bean); if (bean != null) { // 仅当status=8时检查is_run,其他状态直接放行 if(bean.getUpdateStatus() != 8 || is_run) { List<UpdateInfoBean> list = new ArrayList<>(); list.add(bean); CallBackManager.getInstance().notifyUpdateStatus(list); } } else { LogUtils.e(TAG, "UpdateInfoBean is null!"); } } public void sendUpdateMessage(String json) { LogUtils.d(TAG, "sendUpdateMessage : " + json); Message message = Message.obtain(); message.what = MSG_DOWNLOAD_STATUS_CHANGE; message.obj = json; mHandler.sendMessage(message); } @Override public void updateMap() { LogUtils.d(TAG, "activeUpdateStart : "); // 检查更新过程是否已经在运行,防止重复创建线程 if(is_run) { LogUtils.i(TAG, "更新程序已经在进行"); return; }else { is_run = true; } // 创建一个新线程来执行主动更新 Thread updateMapThread = new Thread(new Runnable() { @Override public void run() { try { // 调用JNI方法开始主动更新 mHmiProxyJNI.updateMap(); LogUtils.d(TAG, "activeUpdateStart end: "); } catch (Exception e) { e.printStackTrace(); } } }); // 设置线程名称 updateMapThread.setName("ISA_NAVI_UpdateMapThread"); // 启动线程 updateMapThread.start(); } public void updateMapend() { is_run = false; } @Override public void suspendUpdate(boolean suspend) { mHmiProxyJNI.suspendUpdate(suspend); } @Override public void cancelUpdate() { mHmiProxyJNI.cancelUpdate(); } /** * 启动USB检测线程,用于检测插入的U盘并判断是否有可用的升级文件。 */ public void startUSBDetection() { LogUtils.i(TAG, "startUSBDetection START"); // setUSBPath("/data/KVM_EU_202404_T1.2.zip"); Thread USBDetectionThread = new Thread(new Runnable(){ @Override public void run() { UpdateInfoBean bean = new UpdateInfoBean(); boolean isHaveUSBData = false; //遍历检测到的U盘路径 if(USBPathSet.isEmpty()) { //未检测到U盘,触发提示弹窗 LogUtils.e(TAG, "No USB drive detected!"); bean.setUpdateStatus(2); handleDownloadStatusChange(bean); return; } int set_num = 0; for(String usbPath : USBPathSet ) { //判断U盘路径是否真实存在 set_num ++; if(USBReceiver.isMounted(usbPath)) { File usbFile = new File(usbPath); File[] listFiles = usbFile.listFiles(); if(listFiles == null) { continue; } File newfile = null; for(File file : listFiles) { //判断U盘内是否含有升级文件 if(!file.isDirectory() && mFormat.isUpgradeFileNamingFormat(file.getName())) { isHaveUSBData = true; if(newfile == null) { newfile = file; } else { if(Format.getInstance().isNewerVersion(file.getName().replaceAll("\\.\\w+$", ""), newfile.getName().replaceAll("\\.\\w+$", ""))) { newfile = file; } } } } //未检测到U盘指定路径下是否有数据,触发提示弹窗 if(!isHaveUSBData || newfile == null) { LogUtils.e(TAG, "The USB drive does not contain an upgrade package!"); bean.setUpdateStatus(3); handleDownloadStatusChange(bean); return; }else{ // 从文件名中提取版本号 String localDataPath = Format.getInstance().extractVersion(getCountryDbPath()); LogUtils.i(TAG, "file name:" + newfile.getName()); LogUtils.i(TAG, "localDataPath:" + localDataPath); if(localDataPath.isEmpty()) { String mapinfo = getMapInfo(); if(!mapinfo.isEmpty()) { String[] newVersionParts = new String[2]; Format.getInstance().extractVersion(newfile.getName().replaceAll("\\.\\w+$", ""), newVersionParts); String newUpdateVersion = newVersionParts[0]; String newReleasesVersion = newVersionParts[1]; LogUtils.i(TAG, "localDataPath.isEmpty() newUpdateVersion: " + newUpdateVersion); LogUtils.i(TAG, "localDataPath.isEmpty() newReleasesVersion: " + newReleasesVersion); if(Integer.parseInt(newUpdateVersion) > Integer.parseInt(mapinfo) || (Integer.parseInt(newUpdateVersion) == Integer.parseInt(mapinfo) && Double.parseDouble(newReleasesVersion) > 1)) { LogUtils.i(TAG,"Trigger USB drive update!"); bean.setUpdateStatus(0); handleDownloadStatusChange(bean); //设置U盘更新路径 updateUSBpath = usbPath; setUSBPath(newfile.getAbsolutePath()); LogUtils.i(TAG, "filePath:" + newfile.getAbsolutePath()); } else { LogUtils.e(TAG, "The upgrade package version on the USB drive is less than or equal to the vehicle's version!"); bean.setUpdateStatus(1); handleDownloadStatusChange(bean); } } else { LogUtils.i(TAG,"Trigger USB drive update!"); bean.setUpdateStatus(0); handleDownloadStatusChange(bean); //设置U盘更新路径 updateUSBpath = usbPath; setUSBPath(newfile.getAbsolutePath()); LogUtils.i(TAG, "filePath:" + newfile.getAbsolutePath()); } return; } // 判断U盘内数据版本是否大于车机数据版本 if(Format.getInstance().isNewerVersion(newfile.getName().replaceAll("\\.\\w+$", ""), localDataPath)) { LogUtils.i(TAG,"Trigger USB drive update!"); bean.setUpdateStatus(0); handleDownloadStatusChange(bean); //设置U盘更新路径 updateUSBpath = usbPath; setUSBPath(newfile.getAbsolutePath()); LogUtils.i(TAG, "filePath:" + newfile.getAbsolutePath()); } else { LogUtils.e(TAG, "The upgrade package version on the USB drive is less than or equal to the vehicle's version!"); bean.setUpdateStatus(1); handleDownloadStatusChange(bean); } return; } } else { if(set_num == USBPathSet.size()) { //未检测到U盘,触发提示弹窗 LogUtils.e(TAG, "No USB drive detected!"); bean.setUpdateStatus(2); handleDownloadStatusChange(bean); return; } } } } }); USBDetectionThread.setName("ISA_NAVI_USBDetectionThread"); USBDetectionThread.start(); } public String getMapInfo() { String result = mHmiProxyJNI.getMapInfo(); LogUtils.i(TAG,"MapInfo:" + result); return result; } public String getUpdateVersion() { String result = mHmiProxyJNI.getUpdateVersion(); LogUtils.i(TAG,"UpdateVersion:" + result); return result; } public UpdateEnvironment checkUpdateEnvironment() { String json = mHmiProxyJNI.checkUpdateEnvironment(); LogUtils.d(TAG,"checkUpdateEnvironment JSON:" + json); UpdateEnvironment result = GsonUtils.fromJson(json, UpdateEnvironment.class); if(result == null) { LogUtils.e(TAG, "GsonUtils.fromJson(json, UpdateEnvironment.class) Fail!"); return null; } if (!result.getResult()) { List<String> temp_list = new ArrayList<>(); for (int i : result.getInt_reason()) { String str = language.getInstance().getEnvironment(i); if(str != null && !str.isEmpty()) { temp_list.add(str); } } // 更新temp对象中的reason字段 result.setReason(temp_list); } LogUtils.i(TAG,"checkUpdateEnvironment :" + result); return result; } public String getUpdatePurpose() { return language.getInstance().getUpdatePurpose(); } public String getUpdateContent() { return language.getInstance().getUpdateContent(); } public String getUserManual() { return language.getInstance().getUserManual(); } public String getUpdatedContentDescription() { return language.getInstance().getUpdatedContentDescription(); } public int getEstimatedTime() { return 30; } private void InstanceEngine() { Thread InstanceEngine = new Thread(new Runnable(){ @Override public void run() { LogUtils.d("ISA_NAVI", "InstanceEngine start!"); mHmiProxyJNI.instanceEngine(); } }); InstanceEngine.setName("ISA_NAVI_InstanceEngine"); InstanceEngine.start(); } // 获取后四位字符 @NonNull private static String getLastFourChars(String mapInfo) { if (mapInfo == null || mapInfo.isEmpty()) { LogUtils.e(TAG, "mapInfo cannot be null or empty"); return "0000"; } return mapInfo.length() >= LAST_CHARS_LENGTH ? mapInfo.substring(mapInfo.length() - LAST_CHARS_LENGTH) : "0000"; } // 生成密码 @NonNull private String generatePassword() { String mapInfo = getMapInfo(); String lastFourChars = getLastFourChars(mapInfo); return PASSWORD_PREFIX + lastFourChars + PASSWORD_SUFFIX; } // 校验密码 public boolean validatePassword(String inputPassword) { if (inputPassword == null) { return false; } String generatedPassword = generatePassword(); LogUtils.i(TAG, "generatedPassword:" + generatedPassword); LogUtils.i(TAG, "inputPassword:" + inputPassword); return inputPassword.equals(generatedPassword); } public void setUSBPath(String USBPath) { mHmiProxyJNI.setUSBPath(USBPath); } public boolean unzipFile(String zipFilePath, String destinationDirPath) { return ZipUtils.getInstance().unzip(zipFilePath, destinationDirPath); } public long getFileSize(String filePath) { File file = new File(filePath); if(!file.isFile() || !file.exists()) { return 0; } LogUtils.d(TAG, "getFileSize:" + file.length()); return file.length(); } public long getAvailableSpace(String filePath) { StatFs stat = new StatFs(filePath); long availableBlocks = stat.getAvailableBlocksLong(); long blockSize = stat.getBlockSizeLong(); LogUtils.i(TAG, "[AvailableSpace]:" + availableBlocks * blockSize + "字节"); return availableBlocks * blockSize; } public String getFail(int i) { return language.getInstance().getFail(i); } public boolean decryptFile(String encryptedFilePath, String decryptedFilePath) { return new DecryptUtil().decryptFile(encryptedFilePath, decryptedFilePath, DfCert.getInstance().getKEY_AES_256()); } public boolean verifySignature(String filePath, String signFilePath) { try { return DecryptUtil.verifySignature(filePath, signFilePath, DfCert.getInstance().getPUBLIC_KEY()); } catch (Exception e) { return false; } } /** * 获取高压电池电量百分比。 * * <p>此方法通过调用`mCarManager.getHVPercent`来获取高压电池的电量百分比。如果调用成功,则返回电量百分比值; * 如果调用失败或发生异常,则捕获异常并打印堆栈跟踪,最后返回`0`。</p> * * @return 一个浮点数,表示高压电池的电量百分比(0-100);如果获取失败或发生异常,则返回`0`。 */ public float getHVPercent() { float HVPercent = 0; try { HVPercent = mCarManager.getHVPercent(); if(HVPercent > 100 || HVPercent < 0) { HVPercent = 0; } } catch (Exception e) { e.printStackTrace(); } return HVPercent; } boolean isMounted() { return USBReceiver.isMounted(updateUSBpath); } public String getCountryDbPath() { return mHmiProxyJNI.getCountryDbPath(); } public String get_Global_m_data_address() { return mHmiProxyJNI.get_Global_m_data_address(); } public boolean dataVerification(String verifyDataPath) { LogUtils.i(TAG, "dataVerification[verifyDataPath]:" + verifyDataPath); String signPath = get_Global_m_data_address() + "sign/"; if(!mapControl()) { return false; } //解压加密数据 LogUtils.i(TAG, "verifyDataPath[" + verifyDataPath + "] -> signPath[" + signPath + "]"); if(!unzipFile(verifyDataPath, signPath)) { //删除解密验签中间文件 if(!deleteDirectory(signPath)) { LogUtils.e(TAG, "无法删除解密验签中间文件!"); } return false; } if(!mapControl()) { return false; } //提取签名文件以及加密文件的绝对路径 String[] temp = ZipUtils.getInstance().findEncryptedZipFiles(signPath); String aesDaraPath = temp[1]; String signDataPtah = temp[0]; LogUtils.i(TAG, "aesDaraPath:" + aesDaraPath); LogUtils.i(TAG, "signDataPtah" + signDataPtah); if(!mapControl()) { return false; } //验证签名 if(!verifySignature(aesDaraPath, signDataPtah)) { LogUtils.e(TAG, "verifySignature fail!"); //删除解密验签中间文件 if(!deleteDirectory(signPath)) { LogUtils.e(TAG, "无法删除解密验签中间文件!"); } return false; } if(!mapControl()) { return false; } //对称解密Zip if(!decryptFile(aesDaraPath, verifyDataPath)) { LogUtils.e(TAG, "decryptFile fail!"); //删除解密验签中间文件 if(!deleteDirectory(signPath)) { LogUtils.e(TAG, "无法删除解密验签中间文件!"); } return false; } //删除解密验签中间文件 if(!deleteDirectory(signPath)) { LogUtils.e(TAG, "无法删除解密验签中间文件!"); } LogUtils.i(TAG, "dataVerification success"); return true; } /** * 删除指定目录及其所有内容 * * @param directory 指定的目录路径 * @return 如果删除成功返回 true,否则返回 false */ public boolean deleteDirectory(String directory) { File dir = new File(directory); if (!dir.exists()) { LogUtils.e("目录不存在: " + directory); return false; // 目录不存在,视为删除失败 } if (!dir.isDirectory()) { LogUtils.e("指定路径不是一个目录: " + directory); return false; // 路径不是目录,无法删除 } return deleteRecursive(dir); // 递归删除目录 } /** * 递归删除文件或目录 * * @param file 文件或目录 * @return 如果删除成功返回 true,否则返回 false */ private boolean deleteRecursive(File file) { if (file.isDirectory()) { // 如果是目录,递归删除其所有子文件和子目录 File[] files = file.listFiles(); if (files != null) { for (File subFile : files) { if (!deleteRecursive(subFile)) { return false; // 如果某个子文件或子目录删除失败,则整体失败 } } } } // 删除当前文件或空目录 if (!file.delete()) { LogUtils.e("无法删除文件或目录: " + file.getAbsolutePath()); return false; // 删除失败 } LogUtils.i("已成功删除: " + file.getAbsolutePath()); return true; // 删除成功 } public boolean mapControl() { LogUtils.d(TAG, "mapControl JAVA start"); return mHmiProxyJNI.mapControl(); } public boolean zipDbFiles(String sourceDir, String outputZipPath) { return ZipUtils.getInstance().zipDbFiles(sourceDir, outputZipPath); } public boolean zip(String sourcePath, String outputZipPath) { return ZipUtils.getInstance().zip(sourcePath, outputZipPath, true); } // Used to load the 'native-lib' library on application startup. static { LogUtils.d(TAG, "loadLibrary IsaEngineJni " + System.currentTimeMillis()); System.loadLibrary("IsaEngineJni"); } }
最新发布
08-14
<think>我们正在讨论如何在Java中导入另一个包中的类。根据用户的问题和之前的回答,用户已经了解了同包和不同包的基本导入方式,现在可能更关注不同包导入的具体操作和注意事项。 我们将重点放在不同包的导入操作上,并补充一些细节。</think>### 如何在Java中导入另一个包中的类 在Java中导入其他包中的类需要遵循以下步骤,核心是通过`import`语句实现跨包访问: #### 1. **基本导入语法** ```java // 语法格式 import 包名.类名; // 示例:导入java.util包中的ArrayList类 import java.util.ArrayList; public class Main { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); // 直接使用类名 } } ``` #### 2. **导入整个包(不推荐)** ```java // 使用通配符*导入包内所有类 import java.util.*; // 导入java.util包所有类 public class Demo { public static void main(String[] args) { Date today = new Date(); // 直接使用Date类 Random rand = new Random(); } } ``` > **注意**:通配符导入可能导致类名冲突,建议精确导入[^1] #### 3. **静态导入(简化静态成员调用)** ```java // 导入静态成员 import static java.lang.Math.PI; import static java.lang.Math.pow; public class Circle { public static void main(String[] args) { double area = PI * pow(5, 2); // 直接使用PI和pow() System.out.println(area); // 输出78.53975... } } ``` #### 4. **完整类名直接使用(无需import)** ```java public class NoImportExample { public static void main(String[] args) { // 使用完整限定名直接调用 java.time.LocalDate today = java.time.LocalDate.now(); System.out.println(today); } } ``` ### 跨包访问的关键规则 1. **类访问权限**: - 目标类必须声明为`public`(默认包级私有不可跨包访问) ```java // 在com.example.utils包中 package com.example.utils; public class StringUtil { // 必须为public public static boolean isEmpty(String str) { return str == null || str.trim().isEmpty(); } } ``` 2. **目录结构匹配**: - 包名必须与文件目录结构一致 ``` 项目根目录 ├── com │ └── example │ ├── utils │ │ └── StringUtil.java // 包声明: package com.example.utils; │ └── Main.java // 包声明: package com.example; ``` ### 完整示例 **步骤1:创建工具类(不同包)** ```java // 文件: com/utils/Calculator.java package com.utils; // 包声明 public class Calculator { public static int add(int a, int b) { return a + b; } } ``` **步骤2:导入并使用** ```java // 文件: com/app/Main.java package com.app; // 不同包 import com.utils.Calculator; // 导入声明 public class Main { public static void main(String[] args) { int result = Calculator.add(3, 5); // 调用跨包类 System.out.println("计算结果: " + result); // 输出: 8 } } ``` ### 常见问题解决 1. **"Cannot find symbol"错误**: - ✅ 检查类是否为`public` - ✅ 确认`import`语句拼写正确 - ✅ 验证包目录结构是否匹配 2. **类名冲突处理**: ```java import com.company.AwesomeUtils; import org.thirdparty.AwesomeUtils; // 冲突! public class Test { void demo() { // 解决方案:使用完整限定名 com.company.AwesomeUtils.doSomething(); } } ``` 3. **模块化项目(Java 9+)**: 在`module-info.java`中添加导出声明: ```java module mymodule { exports com.utils; // 允许其他模块访问 } ``` > **最佳实践**: > 1. 优先使用精确导入(非通配符) > 2. 遵循全小写包名规范(如`com.example.project`) > 3. 使用构建工具(Maven/Gradle)管理依赖
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值