Java 访问修饰符:public、protected、private讲解
文章目录前言一、四种访问级别总览1.1 一张表总结1.2 可见性矩阵二、private 封装的基石2.1 核心作用2.2 private 方法的用途2.3 private 用于构造方法2.4 测试代码三、public 对外暴露的接口3.1 核心作用3.2 public 类与非 public 类3.3 测试代码四、protected 继承体系专用4.1 核心作用4.2 protected 的一个重要细节4.3 测试代码五、默认访问级别包私有5.1 核心作用总结前言Java 提供的四种访问修饰符private、默认(包私有)、protected、public。一、四种访问级别总览1.1 一张表总结Java 共有四种访问级别其中三种有关键字一种是默认不写任何修饰符访问级别关键字可访问范围私有private仅本类内部默认包私有不写本类 同包的类受保护protected本类 同包的类 子类跨包也可公开public任何地方1.2 可见性矩阵访问位置private默认protectedpublic本类✓✓✓✓同包其他类✗✓✓✓不同包子类✗✗✓✓不同包无关类✗✗✗✓二、private封装的基石2.1 核心作用private是封装的直接体现把实现细节藏起来只暴露必要的接口。publicclassBankAccount{privatedoublebalance;// 余额不能被外部直接修改// 通过方法控制修改逻辑可以加校验publicvoiddeposit(doubleamount){if(amount0)thrownewIllegalArgumentException(存款金额必须大于0);this.balanceamount;}publicvoidwithdraw(doubleamount){if(amountbalance)thrownewIllegalArgumentException(余额不足);this.balance-amount;}publicdoublegetBalance(){returnbalance;}}如果balance是public任何地方都能account.balance -9999安全性差。2.2private方法的用途private方法通常是内部的工具方法不对外暴露publicclassPasswordUtils{publicStringencrypt(StringrawPassword){StringsaltedaddSalt(rawPassword);// 调用私有方法returnhash(salted);}privateStringaddSalt(Stringpassword){// 加盐逻辑外部不需要知道returnpasswordSALT_2025;}privateStringhash(Stringinput){// 哈希逻辑外部不需要知道returnInteger.toHexString(input.hashCode());}}2.3private用于构造方法private构造方法用于阻止外部直接实例化常见于单例模式控制只有一个实例工具类不需要实例化只有静态方法工厂方法模式强制通过工厂方法创建对象// 工具类禁止实例化publicclassMathUtils{privateMathUtils(){}// 私有构造防止 new MathUtils()publicstaticintadd(inta,intb){returnab;}}// 单例模式publicclassConfigManager{privatestaticfinalConfigManagerINSTANCEnewConfigManager();privateConfigManager(){}// 私有构造publicstaticConfigManagergetInstance(){returnINSTANCE;}}2.4 测试代码publicclassPrivateTest{publicstaticvoidmain(String[]args){BankAccountaccountnewBankAccount();account.deposit(1000);account.withdraw(200);System.out.println(余额account.getBalance());// 800.0// account.balance -9999; // 编译错误balance has private accesstry{account.withdraw(9999);}catch(IllegalArgumentExceptione){System.out.println(异常捕获e.getMessage());// 余额不足}// 工具类无法实例化编译期报错// MathUtils utils new MathUtils(); // 编译错误System.out.println(MathUtils.add(3,4));// 7// 单例ConfigManagerc1ConfigManager.getInstance();ConfigManagerc2ConfigManager.getInstance();System.out.println(c1c2);// true同一个实例}}三、public对外暴露的接口3.1 核心作用public意味着承诺这个方法或类对所有调用方开放一旦对外公开就要保证向后兼容轻易不能改变签名。// 这是一个公开的 API改动签名会影响所有调用方publicinterfacePaymentService{publicPayResultpay(StringorderId,doubleamount);}3.2public类与非public类一个.java文件中只能有一个public类且类名必须与文件名一致。同文件中的其他类只能是默认包私有访问级别// 文件名必须是 Order.javapublicclassOrder{// public 类对外暴露privateOrderItemitem;// 使用同文件的非 public 类}classOrderItem{// 默认访问级别包私有外部模块不可见StringproductName;intquantity;}OrderItem只是Order的实现细节不需要对外暴露用默认访问级别把它藏在包内部。3.3 测试代码// 文件PublicTest.javapublicclassPublicTest{publicstaticvoidmain(String[]args){OrderordernewOrder();// OrderItem item new OrderItem(); // 同包内可以跨包则编译错误}}四、protected继承体系专用4.1 核心作用protected是三个修饰符中最容易被误解的一个。它的设计目的是允许子类访问父类的实现细节同时对外部无关类保持关闭。// 父类假设在 com.animal 包publicabstractclassAnimal{privateStringname;// 子类也不能直接访问protectedintenergy;// 子类可以直接访问外部无关类不能访问publicAnimal(Stringname){this.namename;this.energy100;}protectedvoidbreathe(){// 子类可以调用或重写外部无法直接调用energy-1;System.out.println(name 呼吸消耗能量);}publicabstractvoidmove();// 公开抽象方法子类必须实现}// 子类在 com.animal.impl 包跨包继承publicclassDogextendsAnimal{publicDog(Stringname){super(name);}Overridepublicvoidmove(){breathe();// ✓ 可以调用父类 protected 方法energy-5;// ✓ 可以直接访问父类 protected 字段System.out.println(狗在跑剩余能量energy);}}4.2protected的一个重要细节跨包的子类只能通过自身引用访问父类的protected成员不能通过父类引用访问// 假设 Dog 和 Animal 不在同一个包publicclassDogextendsAnimal{publicvoidtest(AnimalotherAnimal){this.energy50;// ✓ 通过自身引用访问合法// otherAnimal.energy 50; // ✗ 编译错误不能通过父类引用访问}}这个细节很多人不知道面试中也经常考察。原因是protected的语义是子类对自己继承来的部分有访问权而不是子类对所有 Animal 对象都有访问权。4.3 测试代码// com.animal.Animal.javapackagecom.animal;publicabstractclassAnimal{protectedintenergy100;protectedvoidbreathe(){energy--;}publicabstractvoidmove();}// com.animal.impl.Dog.javapackagecom.animal.impl;importcom.animal.Animal;publicclassDogextendsAnimal{Overridepublicvoidmove(){breathe();// ✓ 跨包子类调用父类 protected 方法energy-5;// ✓ 跨包子类访问父类 protected 字段System.out.println(剩余能量energy);}publicvoidtest(Animalother){this.energy80;// ✓ 通过 this// other.energy 80; // ✗ 编译错误}}// 测试入口publicclassProtectedTest{publicstaticvoidmain(String[]args){DogdognewDog();dog.move();// 剩余能量94// dog.energy 999; // ✗ 不同包的无关类编译错误// dog.breathe(); // ✗ 不同包的无关类编译错误}}五、默认访问级别包私有5.1 核心作用不写任何修饰符就是包私有package-private。它的语义是这个类/方法只属于当前包的内部实现不对包外暴露。这是模块内聚的重要工具把一个功能模块的内部实现类全部设为包私有对外只暴露public的接口类。// com.payment 包内部// 对外暴露的接口publicinterfacePaymentProcessor{PayResultprocess(Orderorder);}// 包内部的实现类外部模块不可见classAlipayClient{// 包私有支付宝底层客户端Stringcall(Stringurl){returnok;}}classSignatureUtils{// 包私有签名工具staticStringsign(Stringdata){returndata_signed;}}// 对外暴露的实现publicclassAlipayProcessorimplementsPaymentProcessor{privateAlipayClientclientnewAlipayClient();// 内部使用OverridepublicPayResultprocess(Orderorder){StringsignedSignatureUtils.sign(order.toString());client.call(signed);returnnewPayResult(true);}}外部模块只能看到PaymentProcessor和AlipayProcessorAlipayClient和SignatureUtils完全不可见随时可以重构而不影响外部。总结访问修饰符是 Java 封装性的基础工具用一句话总结每种级别的设计意图修饰符设计意图private这是我的实现细节跟你无关默认这是我们包内部的事外人别插手protected这是给子类预留的扩展点无关类别碰public这是我对外的承诺随时可以调用