You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中读取int变量时出现NullPointerException的原因及修复方案

解决Android中NullPointerException:访问null对象的hourOfDay字段问题

嘿,我来帮你搞定这个烦人的空指针异常问题!先理清楚你的场景:你设置了两个自定义时间选择器,点击按钮3想获取它们的时间值时触发了NullPointerException,报错日志显示你在访问一个null对象的hourOfDay字段。

问题还原

先把你的代码和报错信息整理出来,方便分析:

报错日志

java.lang.NullPointerException: Attempt to read from field 'int com.ahmedco.testcode2.TimePickerDialog.hourOfDay' on a null object reference at com.ahmedco.testcode2.MainActivity$3.onClick(MainActivity.java:65) at android.view.View.performClick(View.java:6256) at android.view.View$PerformClick.run(View.java:24701) at android.os.Handler.handleCallback(Handler.java:789) at android.os.Handler.dispatchMessage(Handler.java:98) at android.os.Looper.loop(Looper.java:164) at android.app.ActivityThread.main(ActivityThread.java:6541) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)

MainActivity代码

public class MainActivity extends AppCompatActivity {
    Button b1, b2, b3;
    DialogFragment timePickerDialog1, timePickerDialog2;
    int hourTimer1, hourTimer2, minuteTimer1, minuteTimer2;
    String time1Format, time2Format, AM_PMTimer1, AM_PMTimer2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        b1 = (Button) findViewById(R.id.button);
        b2 = (Button) findViewById(R.id.button2);
        b3 = (Button) findViewById(R.id.button3);

        b1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                timePickerDialog1 = new TimePickerDialog(1);
                timePickerDialog1.show(getSupportFragmentManager(), "timePicker");
            }
        });

        b2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                timePickerDialog2 = new TimePickerDialog(2);
                timePickerDialog2.show(getSupportFragmentManager(), "timePicker");
            }
        });

        b3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                hourTimer1 = ((TimePickerDialog) timePickerDialog1).hourOfDay;
                hourTimer2 = ((TimePickerDialog) timePickerDialog2).hourOfDay;
                minuteTimer1 = ((TimePickerDialog) timePickerDialog1).minute;
                minuteTimer2 = ((TimePickerDialog) timePickerDialog2).minute;
                AM_PMTimer1 = ((TimePickerDialog) timePickerDialog1).AM_PM;
                AM_PMTimer2 = ((TimePickerDialog) timePickerDialog2).AM_PM;
                time1Format = "" + hourTimer1 + ":" + minuteTimer1 + " " + AM_PMTimer1;
                time2Format = "" + hourTimer1 + ":" + minuteTimer1 + " " + AM_PMTimer2;
                Log.i("time1Format", "" + time1Format);
                Log.i("time2Format", "" + time2Format);
            }
        });
    }
}

TimePickerDialog代码

public class TimePickerDialog extends DialogFragment implements android.app.TimePickerDialog.OnTimeSetListener {
    public String AM_PM = "";
    public int hourOfDay = 0;
    public int minute = 0;

    public TimePickerDialog(int id) {
        // currentEditText = id;
    }

    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        final Calendar c = Calendar.getInstance();
        int hour = c.get(Calendar.HOUR_OF_DAY);
        int minute = c.get(Calendar.MINUTE);
        return new android.app.TimePickerDialog(getActivity(), this, hour, minute, DateFormat.is24HourFormat(getActivity()));
    }

    @Override
    public void onTimeSet(TimePicker view, int hourOfDay_, int minute_) {
        if (hourOfDay_ > 12) {
            AM_PM = "PM";
            hourOfDay_ = hourOfDay_ - 12;
        } else {
            AM_PM = "AM";
        }
        hourOfDay = hourOfDay_;
        minute = minute_;
    }
}

问题根源

  1. 空对象直接访问:如果用户没点击按钮1或2,timePickerDialog1/timePickerDialog2会保持初始的null状态,此时强制转换并访问字段必然触发空指针。
  2. Fragment生命周期坑:就算用户之前打开过时间选择器,对话框关闭后Fragment可能被系统销毁,你的引用会变成无效的空对象,再访问字段也会出错。
  3. 小bug:你拼接time2Format时写错了变量,用了hourTimer1和minuteTimer1,应该对应第二个时间的变量。

修复方案

给你两个方案,第一个快速解决,第二个是Android推荐的最佳实践,更健壮:

方案1:快速修复——添加空值检查

在按钮3的点击事件里先判断对话框实例是否存在,不存在就提示用户先选择时间:

b3.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        // 检查两个时间选择器是否都已初始化
        if (timePickerDialog1 == null || timePickerDialog2 == null) {
            Toast.makeText(MainActivity.this, "请先选择两个时间哦!", Toast.LENGTH_SHORT).show();
            return;
        }
        
        // 提前强转,避免重复转换
        TimePickerDialog dialog1 = (TimePickerDialog) timePickerDialog1;
        TimePickerDialog dialog2 = (TimePickerDialog) timePickerDialog2;
        
        hourTimer1 = dialog1.hourOfDay;
        hourTimer2 = dialog2.hourOfDay;
        minuteTimer1 = dialog1.minute;
        minuteTimer2 = dialog2.minute;
        AM_PMTimer1 = dialog1.AM_PM;
        AM_PMTimer2 = dialog2.AM_PM;
        
        // 修复time2Format的变量错误,用String.format更简洁
        time1Format = String.format("%d:%d %s", hourTimer1, minuteTimer1, AM_PMTimer1);
        time2Format = String.format("%d:%d %s", hourTimer2, minuteTimer2, AM_PMTimer2);
        
        Log.i("time1Format", time1Format);
        Log.i("time2Format", time2Format);
    }
});

方案2:最佳实践——用回调传递数据(彻底避免空指针)

直接访问Fragment的公共字段不是好设计,我们用回调接口让时间选择器主动把数据传给MainActivity,完全不用依赖Fragment实例:

第一步:修改TimePickerDialog,添加回调接口

public class TimePickerDialog extends DialogFragment implements android.app.TimePickerDialog.OnTimeSetListener {
    // 定义回调接口,用于传递时间数据
    public interface OnTimeSelectedListener {
        void onTimeSelected(int dialogId, int hour, int minute, String amPm);
    }

    private OnTimeSelectedListener listener;
    private int dialogId; // 标记是第一个还是第二个时间选择器

    public TimePickerDialog(int id) {
        this.dialogId = id;
    }

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        // 确保宿主Activity实现了回调接口
        try {
            listener = (OnTimeSelectedListener) context;
        } catch (ClassCastException e) {
            throw new ClassCastException(context.toString() + " 必须实现OnTimeSelectedListener接口!");
        }
    }

    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        final Calendar c = Calendar.getInstance();
        int hour = c.get(Calendar.HOUR_OF_DAY);
        int minute = c.get(Calendar.MINUTE);
        return new android.app.TimePickerDialog(getActivity(), this, hour, minute, DateFormat.is24HourFormat(getActivity()));
    }

    @Override
    public void onTimeSet(TimePicker view, int hourOfDay_, int minute_) {
        String amPm;
        int hour = hourOfDay_;
        if (hourOfDay_ > 12) {
            amPm = "PM";
            hour = hourOfDay_ - 12;
        } else {
            amPm = "AM";
        }
        // 选择时间后,通过回调把数据传给MainActivity
        if (listener != null) {
            listener.onTimeSelected(dialogId, hour, minute_, amPm);
        }
    }
}

第二步:修改MainActivity,实现回调接口

public class MainActivity extends AppCompatActivity implements TimePickerDialog.OnTimeSelectedListener {
    Button b1, b2, b3;
    // 直接存储时间数据,无需依赖Fragment实例
    int hourTimer1 = -1, hourTimer2 = -1, minuteTimer1 = -1, minuteTimer2 = -1;
    String AM_PMTimer1 = "", AM_PMTimer2 = "";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        b1 = findViewById(R.id.button);
        b2 = findViewById(R.id.button2);
        b3 = findViewById(R.id.button3);

        b1.setOnClickListener(view -> {
            new TimePickerDialog(1).show(getSupportFragmentManager(), "timePicker1");
        });

        b2.setOnClickListener(view -> {
            new TimePickerDialog(2).show(getSupportFragmentManager(), "timePicker2");
        });

        b3.setOnClickListener(view -> {
            // 检查是否已选择两个时间
            if (hourTimer1 == -1 || hourTimer2 == -1) {
                Toast.makeText(this, "请先选择两个时间哦!", Toast.LENGTH_SHORT).show();
                return;
            }
            String time1Format = String.format("%d:%d %s", hourTimer1, minuteTimer1, AM_PMTimer1);
            String time2Format = String.format("%d:%d %s", hourTimer2, minuteTimer2, AM_PMTimer2);
            Log.i("time1Format", time1Format);
            Log.i("time2Format", time2Format);
        });
    }

    // 实现回调方法,接收时间选择器传来的数据
    @Override
    public void onTimeSelected(int dialogId, int hour, int minute, String amPm) {
        if (dialogId == 1) {
            hourTimer1 = hour;
            minuteTimer1 = minute;
            AM_PMTimer1 = amPm;
        } else if (dialogId == 2) {
            hourTimer2 = hour;
            minuteTimer2 = minute;
            AM_PMTimer2 = amPm;
        }
    }
}

额外说明

  • 方案2不仅解决了空指针问题,还符合Android组件间通信的最佳实践,Fragment不用暴露内部字段,代码更解耦、更健壮。
  • 我已经帮你修复了time2Format的拼接错误,现在会正确使用第二个时间的变量。

内容的提问来源于stack exchange,提问作者hosam hassn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 07:57:29