爆款云主机2核4G限时秒杀,88元/年起!
查看详情

活动

天翼云最新优惠活动,涵盖免费试用,产品折扣等,助您降本增效!
热门活动
  • 618智算钜惠季 爆款云主机2核4G限时秒杀,88元/年起!
  • 免费体验DeepSeek,上天翼云息壤 NEW 新老用户均可免费体验2500万Tokens,限时两周
  • 云上钜惠 HOT 爆款云主机全场特惠,更有万元锦鲤券等你来领!
  • 算力套餐 HOT 让算力触手可及
  • 天翼云脑AOne NEW 连接、保护、办公,All-in-One!
  • 中小企业应用上云专场 产品组合下单即享折上9折起,助力企业快速上云
  • 息壤高校钜惠活动 NEW 天翼云息壤杯高校AI大赛,数款产品享受线上订购超值特惠
  • 天翼云电脑专场 HOT 移动办公新选择,爆款4核8G畅享1年3.5折起,快来抢购!
  • 天翼云奖励推广计划 加入成为云推官,推荐新用户注册下单得现金奖励
免费活动
  • 免费试用中心 HOT 多款云产品免费试用,快来开启云上之旅
  • 天翼云用户体验官 NEW 您的洞察,重塑科技边界

智算服务

打造统一的产品能力,实现算网调度、训练推理、技术架构、资源管理一体化智算服务
智算云(DeepSeek专区)
科研助手
  • 算力商城
  • 应用商城
  • 开发机
  • 并行计算
算力互联调度平台
  • 应用市场
  • 算力市场
  • 算力调度推荐
一站式智算服务平台
  • 模型广场
  • 体验中心
  • 服务接入
智算一体机
  • 智算一体机
大模型
  • DeepSeek-R1-昇腾版(671B)
  • DeepSeek-R1-英伟达版(671B)
  • DeepSeek-V3-昇腾版(671B)
  • DeepSeek-R1-Distill-Llama-70B
  • DeepSeek-R1-Distill-Qwen-32B
  • Qwen2-72B-Instruct
  • StableDiffusion-V2.1
  • TeleChat-12B

应用商城

天翼云精选行业优秀合作伙伴及千余款商品,提供一站式云上应用服务
进入甄选商城进入云市场创新解决方案
办公协同
  • WPS云文档
  • 安全邮箱
  • EMM手机管家
  • 智能商业平台
财务管理
  • 工资条
  • 税务风控云
企业应用
  • 翼信息化运维服务
  • 翼视频云归档解决方案
工业能源
  • 智慧工厂_生产流程管理解决方案
  • 智慧工地
建站工具
  • SSL证书
  • 新域名服务
网络工具
  • 翼云加速
灾备迁移
  • 云管家2.0
  • 翼备份
资源管理
  • 全栈混合云敏捷版(软件)
  • 全栈混合云敏捷版(一体机)
行业应用
  • 翼电子教室
  • 翼智慧显示一体化解决方案

合作伙伴

天翼云携手合作伙伴,共创云上生态,合作共赢
天翼云生态合作中心
  • 天翼云生态合作中心
天翼云渠道合作伙伴
  • 天翼云代理渠道合作伙伴
天翼云服务合作伙伴
  • 天翼云集成商交付能力认证
天翼云应用合作伙伴
  • 天翼云云市场合作伙伴
  • 天翼云甄选商城合作伙伴
天翼云技术合作伙伴
  • 天翼云OpenAPI中心
  • 天翼云EasyCoding平台
天翼云培训认证
  • 天翼云学堂
  • 天翼云市场商学院
天翼云合作计划
  • 云汇计划
天翼云东升计划
  • 适配中心
  • 东升计划
  • 适配互认证

开发者

开发者相关功能入口汇聚
技术社区
  • 专栏文章
  • 互动问答
  • 技术视频
资源与工具
  • OpenAPI中心
开放能力
  • EasyCoding敏捷开发平台
培训与认证
  • 天翼云学堂
  • 天翼云认证
魔乐社区
  • 魔乐社区

支持与服务

为您提供全方位支持与服务,全流程技术保障,助您轻松上云,安全无忧
文档与工具
  • 文档中心
  • 新手上云
  • 自助服务
  • OpenAPI中心
定价
  • 价格计算器
  • 定价策略
基础服务
  • 售前咨询
  • 在线支持
  • 在线支持
  • 工单服务
  • 建议与反馈
  • 用户体验官
  • 服务保障
  • 客户公告
  • 会员中心
增值服务
  • 红心服务
  • 首保服务
  • 客户支持计划
  • 专家技术服务
  • 备案管家

了解天翼云

天翼云秉承央企使命,致力于成为数字经济主力军,投身科技强国伟大事业,为用户提供安全、普惠云服务
品牌介绍
  • 关于天翼云
  • 智算云
  • 天翼云4.0
  • 新闻资讯
  • 天翼云APP
基础设施
  • 全球基础设施
  • 信任中心
最佳实践
  • 精选案例
  • 超级探访
  • 云杂志
  • 分析师和白皮书
  • 天翼云·创新直播间
市场活动
  • 2025智能云生态大会
  • 2024智算云生态大会
  • 2023云生态大会
  • 2022云生态大会
  • 天翼云中国行
天翼云
  • 活动
  • 智算服务
  • 产品
  • 解决方案
  • 应用商城
  • 合作伙伴
  • 开发者
  • 支持与服务
  • 了解天翼云
      • 文档
      • 控制中心
      • 备案
      • 管理中心

      【Java编程思想】读书笔记(二)第六章---第十章

      首页 知识中心 软件开发 文章详情页

      【Java编程思想】读书笔记(二)第六章---第十章

      2025-03-27 10:12:02 阅读次数:7

      class,final,初始化,基类,导出,方法

       

      6.2Java访问权限修饰词

      1. 包访问权限:默认的访问权限(有时也表示成为friendly)
        意味着当前包中的所有的其他类对那个成员都有访问权限,但是对于这个包之外的所有类,这个成员却是private。
      2. public:接口访问权限
        使用关键字public,意味着public之后紧跟着的成员声明自己对每个人都是可用的。
      3. private:你无法访问
        除了包含该成员的类之外,其他任何类都无法访问这个成员。
      4. protected:继承访问权限(extends)
        基类的创建者希望有一个特定的成员,把
        对它的访问权限赋予派生类而不是所有的类,这就需要protected来完成这一工作,并且protected也提供了宝访问权限,也就是说,相同包内的其他类可以访问protected元素。

      第七章:复用类

      7.1 组合语法

      初始化:类中域为基本类型时能够自动被初始化为零,但是对象的引用会被初始化为null。
      惰性初始化:创建时,不初始化,在使用之前判空,若为null则初始化。

       public class Bath{
          	private String s;
          	public String toString(){
          		if(s == null){
          			s = "Joy";
          		}
          		return s;
          	}
          }
      

      7.2 继承语法(extends)

      所有的类都是隐式地从Java的标准根类Object进行继承。导出的类会自动获取基类中所有的域和方法。

      为了继承,一般的规则是将所有的数据成员都指定为private,将所有的方法都指定为public。(protected成员也可以借助导出类来访问)

      无参构造器:基类的构造器总是会被调用,并且在导出类的构造器之前被调用
      有参构造器:导出类的有参构造器必须在构造器中的第一句调用基类构造器,否则编译器报错。

          class Game{
          	Game(int i){
          		print("Game constructor");
          	}
          }
          class BoardGame extends Game{
          	BoardGame(int i){
          		super(i);
          		print("BoardGame constructor");
          	}
          }
          public class Chess extends BoardGame{
          	Chess(int i){
          		super(i);
          		print("Chess constructor");
          	}
          	public static void main(){
          		Chess x = new Chess();
          	}
          }
          /*
          output:
          Game constructor
          BoardGame constructor
          Chess constructor
          */
      

      7.4.2名称屏蔽(重载)

      在导出类中重新定义基类中的方法名称,并不会屏蔽基类中的方法。

      class Homer{
          char doh(char c){
              System.out.println("doh(char)");
              return 'd';
          }
      
          float doh(float f){
              System.out.println("doh(float)");
              return 1.0f;
          }
      }
      
      class Milhouse{ }
      
      class Bart extends Homer{
          //@Override  编译器提示基类中不包含此类方法  不能覆盖
          void doh(Milhouse m){
              System.out.println("doh(Milhouse)");
          }
      }
      
      public class Hide {
          public static void main(String[] args) {
              Bart b = new Bart();
              b.doh(1);
              b.doh('x');
              b.doh(1.0f);
              b.doh(new Milhouse());
          }
      }
      
      /*Output:
      doh(float)
      doh(char)
      doh(float)
      doh(Milhouse)
       */
      

      Java SE5新增加了@Override注解,它并不是关键字,但是可以把它当做关键字使用。当你想要覆写某个方法时,可以选择添加这个注解,在你不留心重载而并非覆写了该方法时,编译器就会生成一条错误消息。

      7.6 protected关键字

      可以将某些事物尽可能对这个世界隐藏起来,但仍然允许导出类的成员访问他们。
      对于任何继承于此类的导出类或者位于同一个包内的类来说,它确实可以访问的。(Protected提佛那个了包内访问权限)

      7.7向上转型

      由于继承可以确保基类中所有的方法在导出类中也同样有效,所以能够向基类发送的所有信息同样也可以向导出类发送。

      如果Instrument类具有一个paly() 方法,那么Wind乐器也将同样具备。这意味着我们可以准确的说Wind对象也是一种类型的INstrument。

      class Instrument{
          public void play(){};
          
          static void tune(Instrument i){
              i.play();
          }
      }
      
      public class Wind extends Instrument{
          public static void main(String[] args) {
              Wind flute = new Wind();
              Instrument.tune(flute); //Upcasting
          }
      }
      

      一个方法是在基类中定义的,参数类型是基类,但是可以在子类中使用基类对象调用这个方法,参数类型可以是子类。这时便把子类参数向上转型为基类。(小往大转,总是安全的)

      练习16:创建一个名为Amphibian的类。由此继承产生一个称为Frog的类。在基类中设置适当的方法。在main()中创建一个Frog并向上转型至Amphibian,然后说明所有的方法都可工作。

      class Amphibian {
          public void moveInWater() {
              System.out.println("Moving in Water");
          }
      
          public void moveOnLand() {
              System.out.println("Moving on Land");
          }
      }
      
      class Frog extends Amphibian {
      }
      public class E16_Frog {
          public static void main(String args[]) {
              Amphibian a = new Frog();
              a.moveInWater();
              a.moveOnLand();
          }
      } /* Output: Moving in Water Moving on Land */
      

      到底该用组合还是继承,一个最清晰的判断方法就是是否需要从新类向基类进行向上转型,如果必须向上转型,则继承是必要的,如果不需要,则应当考虑是否需要继承。

      7.8 final关键字

      7.8.1 final数据

      1. 一个永不改变的编译时常量。
      2. 一个在运行时被初始化的值,而你不希望它被改变。

      对于编译器常量这种情况,编译器可以将常量值带入到任何可能用到的计算式中,也就是说可以在编译时执行计算式。这类常量必须是基本数据类型,并且以关键字final表示。在对这个变量进行定义的时候,必须对其进行赋值。

      典型的对常量进行定义的方式:

      public static final int VALUE_THREE = 39;
      

      定义为public,则可以用于包之外;定义为static,则强调只有一份;定义为final,则说明它是一个常量。
      带有恒定初始值(即编译期常量)的final static 基本类型全用大写字母命名,并且字与字之间用下划线隔开。

      当final作用于一个对象时,代表这个变量用于对象的引用,并且只能引用这一个对象,而不能再次进行引用设置。
      比如当定义一个final变量a数组,就不能再后面将a指向别的数组。

      private final int[] a = {1, 2, 3, 4, 5, 6};
      //a = new int[3];   //编译器会报错
      

      练习18:创建一个含有static final域和final域的类,并说明二者之间的区别。

      class SelfCounter {
          private static int count;
      
          private int id = count++;
      
          public String toString() {
              return "SelfCounter " + id;
          }
      }
      
      class WithFinalFields {
          final SelfCounter scf = new SelfCounter();
          static final SelfCounter scsf = new SelfCounter();
      
          public String toString() {
              return "scf = " + scf + "\nscsf = " + scsf;
          }
      }
      
      public class E18_FinalFields {
          public static void main(String args[]) {
              System.out.println("First object:");
              System.out.println(new WithFinalFields());
              System.out.println("Second object:");
              System.out.println(new WithFinalFields());
          }
      }
      /*
      Output:
      First object:
      scf = SelfCounter 1
      scsf = SelfCounter 0
      Second object:
      scf = SelfCounter 2
      scsf = SelfCounter 0
      *///:~
      

      Because class loading initializes the static final, it has the same value in both instances of WithFinalFields, whereas the regular final’s values are different for each instance.
      因为类装入初始化了静态final,所以它在WithFinalFields的两个实例中具有相同的值,而常规final的值对于每个实例是不同的。

      空白final
      Java允许生成空白的final,所谓空白final是指被声明为final但又为给定初始值的域。无论什么情况下,编译器都确保空白final在使用前必须被初始化。

      必须在域的定义处或者每个构造器中用表达式对final进行赋值,这正是final域在使用前总是被初始化的原因所在。

      final参数
      Java允许在参数列表中以声明的方式将参数指明为final,这意味着你无法在方法中更改参数引用所指向的对象:

      class Gizmo{
          public void spin(){}
      }
      
      public class FinalArguments {
          void with(final Gizmo g){
              //g = new Gizmo();  //不能给final类型的引用对象指定新的引用
          }
          void without(Gizmo g){
              g = new Gizmo();
              g.spin();
          }
      
          void f(final int i){
              //i++;  //final的基本类型变量,值不能被更改
          }
          int g(final int i){return i + 1;}  // 可以被读取到
      
          public static void main(String[] args) {
              FinalArguments bf = new FinalArguments();
              bf.without(null);
              bf.with(null);
          }
      }
      

      final和private关键字
      类中所有的private方法都隐式地指定为final的。由于无法取用private方法,所以也就无法覆盖它。可以对private方法添加final修饰词,但是并不能给该方法增加任何额外的意义。

      如果你视图覆盖一个private方法(隐含是final的),似乎是凑效的,而且编译器也不会给出错误信息,但是调用的时候会发现,private方法无法调用。

      class WithFinals{
          private final void f(){
              System.out.println("WithFinals.f()");
          }
          private void g(){
              System.out.println("WithFinals.g()");
          }
      }
      
      class OverridingPrivate extends WithFinals{
          //@Override
          private final void f(){
              System.out.println("Overriding.f()");
          }
          private void g(){
              System.out.println("Overriding.g()");
          }
      }
      
      class OverridingPrivate2 extends OverridingPrivate{
          public final void f(){
              System.out.println("OverridingPrivate2.f()");
          }
          public void g(){
              System.out.println("OverridingPrivate2.g()");
          }
      }
      
      public class FinalOverridingIllusion {
          public static void main(String[] args) {
              OverridingPrivate2 op2 = new OverridingPrivate2();
              op2.f();
              op2.g();
      
              //OverridingPrivate op = op2;
              OverridingPrivate op = new OverridingPrivate();
              //op.f();  // 提示该方法在类中定义为private
              //op.g();
              WithFinals wf = op2;
              //wf.f();
              //wf.g();
          }
      }
      

      “覆盖”只有在某方法是基类的接口的一部分时才会出现。即,必须能将一个对象向上转型为它的基本类型并调用相同的方法。如果某方法是private,它就不是基类的接口的一部分。它仅是一些隐藏于类中的程序代码,只不过是具有相同的名称而已。但如果再导出类中以相同的名称生成一个public、protected或包访问权限方法的话,该方法就不会产生在基类中出现的“仅具有相同名称”的情况。此时你并没有覆盖该方法,仅是生成了一个新的方法。由于private方法无法触及而且能有效隐藏,所以除了把它看成是因为它所归属的类的组织结构的原因而存在外,其他任何事物都不需要考虑到它。

      **问题???**:在练习20中,说使用@Overriding注解可以解决上面的问题。 但是我加了注解之后仍然不能调用,不知道哪里的问题。

      7.8.3 final类
      当将某个类的整体定义为final时(通过将关键字final置于它的定义之前),就表明了你不打算继承该类,而且也不允许别人这样做。

      final类的域可以根据个人的意愿选择是或不是final类型,如果不是final类型,则仍然可以修改其值。

      由于final类禁止继承,所以final类中的所有方法都隐式指定为是final的,因为无法覆盖它们。

      class SmallBrain{
      }
      
      final class Dinosaur{
          final int i = 7;
          int j = 1;
          SmallBrain x = new SmallBrain();
          void f(){}
      }
      
      //class Further extends Dinosaur{}  // 提示不可继承
      
      public class Jurassic {
          public static void main(String[] args) {
              Dinosaur n = new Dinosaur();
              n.f();
              //n.i = 40;  //final类型的域不可更改
              System.out.println(n.i);
              n.j++;
              System.out.println(n.j);
          }
      }
      

      7.9 初始化及类的加载

      一般来说,类的代码在初次使用时才会加载,这通常是指加载发生于创建类的第一个对象之时,但是当访问static域或者static方法时,也会发生加载。

      初次使用之处也是static初始化发生之处。所有的static对象和static代码段都会在加载时依程序中的顺序(即,定义类时的书写顺序)而一次初始化。当然定义为static的东西只会被初始化一次。

      7.9.1 类的继承与初始化

      了解包括继承在内的初始化全过程,以对所有发生的一切有个全局性的把握,是很有益的。

      class Insert {
          private int i = 9;
          protected  int j;
          Insert(){
              System.out.println("i = " + i + ", j = " + j);
              j = 39;
          }
          private static int x1 = printInit("static Insert.x1 initialized");
      
          static int printInit(String s){
              System.out.println(s);
              return 47;
          }
      }
      
      public class Beetle extends Insert{
          private int k = printInit("Beetle.k initialized");
          public Beetle(){
              System.out.println("k = " + k);
              System.out.println("j = " + j);
          }
          private static int x2 = printInit("static Beetle.x2 initialized");
      
          public static void main(String[] args) {
              System.out.println("Beetle constructor");
              Beetle b = new Beetle();
          }
      }
      /*
      static Insert.x1 initialized
      static Beetle.x2 initialized
      Beetle constructor
      i = 9, j = 0
      Beetle.k initialized
      k = 47
      j = 39
       */
      

      在Beetle上运行java时,所发生的第一件事情就是试图访问Beetle.main()(一个static方法),于是加载器开始启动并找出Beetle类的编译代码(在名为Beetle.class的文件中)。对它进行加载的过程中,编译器注意到它有一个基类(这是由关键字extends得知的),于是它继续进行加载。不管你是否打算产生一个该基类的对象,这都要发生(请尝试将对象创建代码注释掉,以证明这一点)。

      如果该基类还有其自身的基类,那么第二个基类就会被加载,以此类推。接下来基类中的static初始化(此例中为Insect)即会被执行,然后是一个导出类,以此类推。这种方式很重要,因为导出类的static初始化可能会依赖基类成员能否正确初始化。

      至此为止,必要的类已加载完毕,对象的创建就可以进行了。首先,对象中所有的基本类型都会被设为默认值,对象引用被设为null—这是通过将对象对象内存设为二进制零值而一举生成的。然后,基类的构造器会被调用。在本例中,它是被自动调用的。但也可以用super来指定对基类构造器的调用(正如在Beetle()构造器中的第一步操作)。基类构造器和导出类构造器一样,以相同的顺序来经历相同的过程。在基类构造器完成后,实例变量按其次序被初始化。自后构造器的其余部分被执行(???构造器和实例变量谁先执行)。

      个人总结:首先是执行main方法,加载器开始加载编译代码class文件,由extends发现有基类,先初始化基类的static域,static域初始化之后,开始对象创建,先将对象中所有的基本类型设为默认值(这里的基本类型理解为int i = 0这种的比较好理解,上面例子中的k初始化在构造器之前了),然后是构造器的执行(基类构造器先执行),构造器执行结束,实例变量再按顺序初始化。

      第八章:多态

      多态的作用则是消除类型之间的耦合关系。

      8.1 再论向上转型

      演奏乐符:Note(枚举类型)

      public enum Note {
          MIDDLE_C, C_SHARP, B_FLAT;
      }
      

      Wind是一种Instrument,因此可以从Instrument类继承。

      class Instrument{
          public void play(Note n){
              System.out.println("Instrument.play()");
          }
      }
      
      public class Wind extends Instrument {
          public void play(Note n){
              System.out.println("Wind.play() " + n);
          }
      }
      
      public class Music {
          public static void tune(Instrument i){
              i.play(Note.MIDDLE_C);
          }
      
          public static void main(String[] args) {
              Wind flute = new Wind();
              tune(flute);
          }
      }
      /*OutPut:
      Wind.play() MIDDLE_C
      */
      

      Music.tunr()方法接受一个Instrument引用,同时也接受任何导出自Instrument的类。在main() 方法中,当一个Wind引用传递到tune()方法时,就会出现这种情况,而不需要任何类型转换。这样做是允许的----因为Wind从Instrument继承而来,所以Instrument的接口必定存在于wind中。从Wind向上转型到Instrument可能会缩小接口,但不会比Instrument的全部接口更窄。

      练习10: 创建一个包含两个方法的基类,在第一个方法中可以调用第二个方法。然后产生一个继承自该基类的导出类,且覆盖基类中的第二个方法。为该导出类创建一个对象,将它向上转型到基类型并调用第一个方法,解释发生的情况。

      class TwoMethods {
          public void m1() {
              System.out.println("Inside m1, calling m2");
              m2();
          }
      
          public void m2() {
              System.out.println("Inside m2");
          }
      }
      
      class Inherited extends TwoMethods {
          public void m2() {
              System.out.println("Inside Inherited.m2");
          }
      }
      
      public class E10_MethodCalls {
          public static void main(String args[]) {
              TwoMethods x = new Inherited();
              x.m1();
          }
      }
      /* Output:
      Inside m1, calling m2
      Inside Inherited.m2
      *///:~
      

      可以看出导出类只覆盖了基类的第二种方法,在向上转型之后,基类对象x执行方法1时,由于导出类为覆盖方法一,则直接执行基类中的方法一,而方法一又调用了方法二,这时导出类中又覆盖了方法二,因此会执行导出类的方法二,而不会执行基类的方法二。对于导出类而言,基类中的方法二已经被覆盖掉了,而方法一作为直接继承过来的方法,可以直接使用。

      8.2.4 缺陷:“覆盖”私有方法(private)

      public class PrivateOverriding {
          private void f(){
              System.out.println("private f()");
          }
      
          public static void main(String[] args) {
              PrivateOverriding po = new Derived();
              po.f();
          }
      }
      
      class Derived extends PrivateOverriding{
          public void f(){
              System.out.println("public f()");
          }
      }
      /* Output:
      private f()
      *///:~
      

      这里会发现一个很神奇的东西,输出的结果竟然是基类的方法,而不是导出类的方法。
      这是由于private方法被自动认为是final方法,而且对导出类是屏蔽的。因此,在这种情况下,Derived类中的f()方法就是一个全新的方法;既然基类中的方法f() 方法在子类Derived中不可见,因此也不能被重载。

      结论就是:只有非private方法才可以被覆盖;但是还需要密切注意覆盖private方法的现象,这时编译器不会报错,但是也不会按照我们所期望的来执行。确切的说,在导出类中,对于基类中的private方法,最好采用不同的名字。

      8.2.5 缺陷:域与静态方法

      class Super{
          public int field = 0;
          public int getField(){
              return field;
          }
      }
      
      class Sub extends Super{
          public int field = 1;
          public int getField(){
              return field;
          }
          public int getSuperField(){
              return super.field;
          }
      }
      
      public class FieldAccess {
          public static void main(String[] args) {
              Super sup = new Sub();
              System.out.println("sup.field = " + sup.field + ", sup.getField = " + sup.getField());
              Sub sub = new Sub();
              System.out.println("sub.field = " + sub.field + ", sub.getField = " + sub.getField() + ", sub.getSuperField = " + sub.getSuperField());
          }
      }
      /* Output:
      sup.field = 0, sup.getField = 1
      sub.field = 1, sub.getField = 1, sub.getSuperField = 0
      *///:~
      

      当Sub对象转型为Super引用时,任何域访问操作都将由编译器解析,因此不是多态的。

      在本例中,为Super.field和Sub.field分配了不同的存储空间。这样,Sub实际上包含两个称为field的域:它自己的域和它从Super处得到的。然而,在引用Sub中的field时所产生的默认域并非Super版本的field域。因此,为了得到Super.field,必须显式地指明super.field.

      如果某个方法是静态的,它的行为就不具有多态性。

      class StaticSuper{
          public static String staticGet(){
              return "Base staticGet()";
          }
      
          public String dynamicGet(){
              return "Base dynamicGet()";
          }
      }
      
      class StaticSub extends StaticSuper{
          public static String staticGet(){
              return "Derived staticGet()";
          }
      
          public String dynamicGet(){
              return "Derived dynamicGet()";
          }
      }
      
      public class StaticPolymorphism {
          public static void main(String[] args) {
              StaticSuper sup = new StaticSub();
              System.out.println(sup.staticGet());
              System.out.println(sup.dynamicGet());
          }
      }
      /* Output:
      Base staticGet()
      Derived dynamicGet()
      *///:~
      

      8.3 构造器和多态

      8.3.1构造器的调用顺序

      class Meal{
          Meal(){
              System.out.println("Meal()");
          }
      }
      
      class Bread{
          Bread(){
              System.out.println("Bread()");
          }
      }
      
      class Chess{
          Chess(){
              System.out.println("Chess()");
          }
      }
      
      class Lettuce{
          Lettuce(){
              System.out.println("Lettuce()");
          }
      }
      
      class Lunch extends Meal{
          Lunch(){
              System.out.println("Lunch()");
          }
      }
      
      class PortableLunch extends Lunch{
          PortableLunch(){
              System.out.println("PortableLunch()");
          }
      }
      
      public class Sandwich extends PortableLunch {
          private Bread b = new Bread();
          private Chess c = new Chess();
          private Lettuce l = new Lettuce();
          public Sandwich(){
              System.out.println("Sandwitch()");
          }
          public static void main(String[] args) {
              new Sandwich();
          }
      }
      /* Output:
      Meal()
      Lunch()
      PortableLunch()
      Bread()
      Chess()
      Lettuce()
      Sandwitch()
      *///:~
      

      复杂对象调用构造器要遵照下面的顺序:

      1. 调用基类构造器。这个步骤会不断地反复递归下去,首先是构造这种层次的跟,然后是下一层导出类,等等,直到最底层的导出类。
      2. 按照声明顺序调用成员的初始化方法。
      3. 调用导出类构造器的主体。

      8.5.2 向下转型和运行时类型识别

      向上转型会丢失具体的类型信息,是安全的。
      向下转型失败时,会跑出一个类转型异常(ClassCastException)

      class Useful{
          public void f(){
              System.out.println("Useful.f()");
          }
          public void g(){
              System.out.println("Useful.g()");
          }
      }
      
      class MoreUseful extends Useful{
          public void f(){
              System.out.println("MoreUseful.f()");
          }
          public void g(){
              System.out.println("MoreUseful.g()");
          }
          public void u(){
              System.out.println("MoreUseful.u()");
          }
      }
      
      public class RTTI {
          public static void main(String[] args) {
              Useful[] x = {new Useful(), new MoreUseful()};
              x[0].f();
              x[1].g();
              ((MoreUseful)x[1]).u();  // Downcast /RTTI
              //((MoreUseful)x[0]).u();  //ClassCastException: char8.Useful cannot be cast to char8.MoreUseful
          }
      }
      /*
      Output:
      Useful.f()
      MoreUseful.g()
      MoreUseful.u()*/
      

      第九章:接口

      9.1抽象类和抽象方法

      抽象类的目的是为它的所有的导出类创建一个通用接口。不同的子类可以用不同的方法会表示此接口。

      抽象方法:仅有声明而没有方法体。 abstract void f();

      包含抽象方法的类叫做抽象类,如果一个类包含一个或多个抽象方法,该类必须被限定为抽象的(abstract),否则编译器会报错。

      如果一个抽象类继承,并不想创建该新类的对象,那么就必须为基类中的所有抽象方法提供方法定义。如果不那么做(可以选择不做),那么该导出类便也是抽象类,并且编译器会强制我们使用abstract关键字来限定该类。

      抽象类中的方法并不需要所有的方法都是抽象的,仅需将我们需要的方法声明为抽象即可。其他的非抽象方法需要有方法体。

      练习2:创建一个抽象类,并验证不能被实例化。

      abstract class NoAbstractMethod{
          void f(){
              System.out.println("f()");
          }
      }
      
      public class E02_Abstract {
          public static void main(String[] args) {
              //new NoAbstractMethod();  //编译器会报错,抽象类,不能被实例化
          }
      }
      

      练习3:创建一个基类,让他包含抽象方法print(),并在导出类中覆盖改方法。覆盖后的方法版本可以打印出导出类中定义的某个整形变量的值。在定义该变量之处,赋予它非零值。在基类的构造方法中调用这个方法。现在,在main() 方法中创建一个导出类对象,然后调用它的print方法。解释这种情况。

      abstract class BaseWithPrint {
          public BaseWithPrint() {
              print();
          }
      
          public abstract void print();
      }
      
      class DerivedWithPrint extends BaseWithPrint {
          int i = 47;
      
          public void print() {
              System.out.println("i = " + i);
          }
      }
      
      public class E03_Initialization {
          public static void main(String args[]) {
              DerivedWithPrint dp = new DerivedWithPrint();
              dp.print();
          }
      } /* Output: 
      i = 0 
      i = 47 
      *///
      

      The java virtual machine zeroes the bits of the object after it allocates storage, producing a default value for i before any other initialization occurs. The code calls the base-class constructor before running the derived-class initialization, so we see the zeroed value of i as the initial output.
      The danger of calling a method inside a constructor is when that method depends on a derived initialization. Before the derived-class constructor is called, the object may be in an unexpected state (in Java, at least that state is defined; this is not true with all languages – C++, for example). The safest approach is to set the object into a known good state as simply as possible, and then perform any other operations outside the constructor.
      翻译:java虚拟机在分配存储之后对对象的位进行归零,在任何其他初始化发生之前为i生成一个默认值。代码在运行派生类初始化之前调用基类构造函数,因此我们将i的零值作为初始输出。

      在构造函数中调用方法的危险在于该方法依赖于派生的初始化。在调用派生类构造函数之前,对象可能处于意外状态(在Java中,至少定义了该状态;并不是所有的语言都是这样,例如c++)。最安全的方法是尽可能简单地将对象设置为已知的良好状态,然后在构造函数之外执行任何其他操作。

      这是第八章中的关于初始化的知识:
      初始化的实际过程:

      1. 在其他任何事物发生之前,将分配给对象的存储空间初始化为二进制的零。
      2. 如前述那样调用基类构造器。此时,调用被覆盖后的print()方法,由于步骤一的缘故,我们此时会发现i的值为0.
      3. 按照声明的顺序调用成员的初始化方法。
      4. 调用导出类的构造器主体。

      编写构造器时有一条有效的准则:“用尽可能简单的方法使对象进入正常状态;如果可以的话避免调用其他方法”。在构造器内唯一安全调用的那些方法是基类中的final方法(也适用于private方法,他们自动属于finale方法)。这些方法不能被覆盖,因此也就不会出现上述的问题。

      9.4 Java中的多重继承(区分extends和implements的使用)

      关键字解释:
      extends:继承;指的是同一类型的,接口与接口之间或者基类与子类之间使用,为扩展性。
      implements:实现;类实现接口时使用。
      使用接口的核心原因:为了能够向上转型为多个基类型(以及由此带来的灵活性)。第二个原因却是与抽象基类相同:防止客户端程序员创建该类的对象,并确保这仅仅建立一个接口。

      这就带来了一个问题:我们应该使用接口还是抽象类?

      如果要创建不带任何方法定义和成员变量的基类,那么就应该选择接口而不是抽象类。事实上,如果知道某事物应该成为一个基类,那么第一选择应该是使它成为一个接口。

      练习13:创建一个接口,并从该接口继承两个接口,然后从后面两个接口多重继承第三个接口。

      interface BaseInterface {
          void f();
      }
       
      interface IntermediateInterface1 extends BaseInterface {
          void f();
      }
       
      interface IntermediateInterface2 extends BaseInterface {
          void f();
      }
       
      interface CombinedInterface extends IntermediateInterface1, IntermediateInterface2 {
          void f();
      }
       
      class CombinedImpl implements CombinedInterface {
          public void f() {
              System.out.println("CombinedImpl.f()");
          }
      }
       
      public class E13_Diamond {
          public static void main(String[] args) {
              new CombinedImpl().f();
          }
      }
          
      /* Output: CombinedImpl.f() *///:~
      

      一般情况下,只可以将extends用于单一类,但是可以引用多个基类接口。就像上面一样,只需用逗号将接口名一一分开即可。

      第十章 内部类

      看的有点麻木了,暂时先跳过了。。。。。。。。。

      版权声明:本文内容来自第三方投稿或授权转载,原文地址:https://blog.51cto.com/linmengmeng/5907343,作者:忘却世俗的人,版权归原作者所有。本网站转在其作品的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如因作品内容、版权等问题需要同本网站联系,请发邮件至ctyunbbs@chinatelecom.cn沟通。

      上一篇:设计模式-原型模式

      下一篇:C#读取shp的属性表dbf文件

      相关文章

      2025-05-19 09:04:44

      使用DoubleLinkedList扩展类,允许Add,Remove,Contains

      使用DoubleLinkedList扩展类,允许Add,Remove,Contains

      2025-05-19 09:04:44
      class , list , null , 扩展
      2025-05-19 09:04:30

      判断dmp文件的导出方式是exp or expdp

      判断dmp文件的导出方式是exp or expdp

      2025-05-19 09:04:30
      expdp , 导出 , 文件
      2025-05-19 09:04:22

      外设驱动库开发笔记54:外设库驱动设计改进的思考

      外设驱动库开发笔记54:外设库驱动设计改进的思考

      2025-05-19 09:04:22
      使用 , 函数 , 初始化 , 定义 , 对象
      2025-05-14 10:33:16

      30天拿下Rust之字符串

      在Rust中,字符串是一种非常重要的数据类型,用于处理文本数据。Rust的字符串是以UTF-8编码的字节序列,主要有两种类型:&str和String。其中,&str是一个对字符数据的不可变引用,更像是对现有字符串数据的“视图”,而String则是一个独立、可变更的字符串实体。

      2025-05-14 10:33:16
      amp , Rust , str , String , 使用 , 字符串 , 方法
      2025-05-14 10:33:16

      30天拿下Rust之向量

      在Rust语言中,向量(Vector)是一种动态数组类型,可以存储相同类型的元素,并且可以在运行时改变大小。向量是Rust标准库中的一部分,位于std::vec模块中。

      2025-05-14 10:33:16
      Rust , 使用 , 元素 , 向量 , 方法 , 索引 , 迭代
      2025-05-14 10:33:16

      C++ 11新特性之tuple

      在C++编程语言的发展历程中,C++ 11标准引入了许多开创性的新特性,极大地提升了开发效率与代码质量。其中,tuple(元组)作为一种强大的容器类型,为处理多个不同类型的值提供了便捷的手段。

      2025-05-14 10:33:16
      std , 元素 , 函数 , 初始化 , 模板 , 类型
      2025-05-14 10:03:05

      30天拿下Rust之HashMap

      HashMap,被称为哈希表或散列表,是一种可以存储键值对的数据结构。它使用哈希函数将键映射到存储位置,以便可以快速检索和更新元素。

      2025-05-14 10:03:05
      HashMap , 使用 , 哈希 , 引用 , 方法 , 遍历 , 键值
      2025-05-14 10:03:05

      C++ 11新特性之语法甜点1

      C++ 11新特性之语法甜点1

      2025-05-14 10:03:05
      函数 , 初始化 , 序列 , 引入 , 构造函数 , 语法
      2025-05-14 10:03:05

      C++ 11新特性之语法甜点2

      C++ 11新特性之语法甜点2

      2025-05-14 10:03:05
      函数 , 初始化 , 基类 , 构造函数 , 编译器 , 语法
      2025-05-14 10:02:58

      30天拿下Rust之结构体

      在Rust语言中,结构体是一种用户自定义的数据类型,它允许你将多个相关的值组合成一个单一的类型。结构体是一种复合数据类型,可以用来封装多个不同类型的字段,这些字段可以是基本数据类型、其他结构体、枚举类型等。通过使用结构体,你可以创建更复杂的数据结构,并定义它们的行为。

      2025-05-14 10:02:58
      Rust , 使用 , 字段 , 实例 , 方法 , 示例 , 结构
      查看更多
      推荐标签

      作者介绍

      天翼云小翼
      天翼云用户

      文章

      33561

      阅读量

      5253162

      查看更多

      最新文章

      外设驱动库开发笔记54:外设库驱动设计改进的思考

      2025-05-19 09:04:22

      30天拿下Rust之向量

      2025-05-14 10:33:16

      C++ 11新特性之tuple

      2025-05-14 10:33:16

      C++ 11新特性之语法甜点1

      2025-05-14 10:03:05

      C++ 11新特性之语法甜点2

      2025-05-14 10:03:05

      java 判断map为null或者空

      2025-05-14 09:51:21

      查看更多

      热门文章

      Python爬虫:scrapy爬虫设置随机访问时间间隔

      2023-02-15 10:02:19

      kubectl源码分析之explain

      2023-02-16 08:23:43

      Java中创建对象的方式

      2023-02-13 09:25:28

      ESLint:可组装的JavaScript和JSX检查工具

      2023-02-23 07:57:25

      kubectl源码分析之rollout undo

      2023-02-16 08:53:34

      java之十一 Java GUI

      2024-09-25 10:13:34

      查看更多

      热门标签

      java Java python 编程开发 代码 开发语言 算法 线程 Python html 数组 C++ 元素 javascript c++
      查看更多

      相关产品

      弹性云主机

      随时自助获取、弹性伸缩的云服务器资源

      天翼云电脑(公众版)

      便捷、安全、高效的云电脑服务

      对象存储

      高品质、低成本的云上存储服务

      云硬盘

      为云上计算资源提供持久性块存储

      查看更多

      随机文章

      Python全网最全基础课程笔记(八)——字典,跟着思维导图和图文来学习,爆肝2w字,无数代码案例!

      Java ClassLoader机制

      详细分析@FunctionalInterface的基本知识(附Demo)

      Python 设计和历史的 27 个问题

      讲下代码生成器原理。

      JavaSE—面向对象 (零基础一条龙式讲解)

      • 7*24小时售后
      • 无忧退款
      • 免费备案
      • 专家服务
      售前咨询热线
      400-810-9889转1
      关注天翼云
      • 旗舰店
      • 天翼云APP
      • 天翼云微信公众号
      服务与支持
      • 备案中心
      • 售前咨询
      • 智能客服
      • 自助服务
      • 工单管理
      • 客户公告
      • 涉诈举报
      账户管理
      • 管理中心
      • 订单管理
      • 余额管理
      • 发票管理
      • 充值汇款
      • 续费管理
      快速入口
      • 天翼云旗舰店
      • 文档中心
      • 最新活动
      • 免费试用
      • 信任中心
      • 天翼云学堂
      云网生态
      • 甄选商城
      • 渠道合作
      • 云市场合作
      了解天翼云
      • 关于天翼云
      • 天翼云APP
      • 服务案例
      • 新闻资讯
      • 联系我们
      热门产品
      • 云电脑
      • 弹性云主机
      • 云电脑政企版
      • 天翼云手机
      • 云数据库
      • 对象存储
      • 云硬盘
      • Web应用防火墙
      • 服务器安全卫士
      • CDN加速
      热门推荐
      • 云服务备份
      • 边缘安全加速平台
      • 全站加速
      • 安全加速
      • 云服务器
      • 云主机
      • 智能边缘云
      • 应用编排服务
      • 微服务引擎
      • 共享流量包
      更多推荐
      • web应用防火墙
      • 密钥管理
      • 等保咨询
      • 安全专区
      • 应用运维管理
      • 云日志服务
      • 文档数据库服务
      • 云搜索服务
      • 数据湖探索
      • 数据仓库服务
      友情链接
      • 中国电信集团
      • 189邮箱
      • 天翼企业云盘
      • 天翼云盘
      ©2025 天翼云科技有限公司版权所有 增值电信业务经营许可证A2.B1.B2-20090001
      公司地址:北京市东城区青龙胡同甲1号、3号2幢2层205-32室
      • 用户协议
      • 隐私政策
      • 个人信息保护
      • 法律声明
      备案 京公网安备11010802043424号 京ICP备 2021034386号