• Java


    初学者如果不理解面向对象的概念可以看看我的另一篇博客:https://www.cnblogs.com/yangyuanhu/p/11287038.html

    对象的内存

    变量定义在栈中,而具体的对象数据则在堆区

    构造方法

    构造函数,构造器

    语法要求

    1.函数名称与类名一致
    2.不允许有返回值类型 
    3.不允许使用return语句返回数据
    

    特点:

    new 对象时会自动执行 
    1.可以重载多个构造方法
    2.当我们没有定义任何构造方法时,会自动生成无参的空构造方法
    3.一旦我们定义类构造方法,系统将不会自动生成
    

    构造方法只能通过两种方式来调用:

    1.在构造方法中可通过this去调用其他的构造方法
    2.通过new 关键字  
    //在普通方法中也不能去掉用构造方法 
    public class Constructer {
        public Constructer(){
        }
        public Constructer(String name){
            this();  //调用空参数构造方法
        }
    }
    

    注意注意:调用其他构造方法只能位于方法的第一行语句;

    方法参数查找采取就近原则

    当对象创建时对象的所有属性都会被自动初始化

    ================================

    封装

    特点:

    1.只能通过规定的接口(方法)访问内部数据

    2.隐藏了内部实现细节,为了隔离复杂度,和提高安全性,提高了类内部的维护性

    使用:

    修改方法或属性的权限为private 私有化

    为私有的属性提供对外开放(public)的setter 和getter方法;

    public class Test {
    
        private String name;//该属性被私有化 此时该属性尽在该类内部可以访问
    
        public Test(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    

    概念:

    包的本质是一个文件夹

    当一个项目中出现大量的类是,维护性会降低,我们可以采取分包的方式来管理 ,将相关的类放到同一个包中,方便管理维护代码,

    命名规范:

    采用域名倒叙,+模块名称+功能名称

    全小写

    image-20191119101847961

    注意:不同包中可以存在相同名称的类

    import

    import用于导入某个类

    语法:

    1.导入某个包下的某个类 import com.yh.package1.Cat;
    2.导入某个包下的所有类 import com.yh.package2.*;

    注意:
    1.当上述两条语句出现了相同的类名称时(package2中也有Cat),优先使用的是语义更清晰的即import com.yh.package1.Cat
    2.第二种语法,仅能导入包下的类,不能导入包下的子包

    我们也可以在代码中使用完全限定类名来避免冲突:

    com.yh.package1.Cat c1 = new com.yh.package1.Cat();
    c1.show();
    

    static

    static修饰的成员称之为静态成员

    特点:

    1.可以使用类名直接调用(当然对象也可以)

    2.并且static修饰的成员属于类名称空间中,也就是说所有该类对象,共享该资源,相反的如果非static修饰的成员是每个对象独有的,例如每个人的name属性不同;

    3.静态成员的生命周期跟随的类,类加载时被加载,虚拟机结束时被销毁;

    4.在静态方法中不可以使用this关键字,因为static修饰的成员比对象先被加载

    5.静态方法中只能访问静态成员

    代码块

    使用一对大括号产生一个代码块,当然了方法也是代码块

    1.构造代码块

    我们还可以在类中直接使用{}来定义出一个构造代码快 像下面这样

    class Person(){
      {
        //构造代码块,会在创建类的时候先于构造函数执行,没创建一个对象就执行一次
      }  
    }
    

    2.普通代码块

    class Person{
      void show(){
        //方法体    
        
        {
          // 普通代码块
        }
        
        //方法体
      }
    }
    

    3.静态代码块

    在类中使用static来修饰的代码块就是静态代码块,其执行时机是在类被加载时,执行一次,后续不在执行,

    什么时候使用?

    当我们需要在使用类之前做一些初始化操作时

    问题?

    1.代码块是否是局部作用域 ? 是的每个代码块都是局部的

    2.静态代码块中是否可以存在方法? 不可以局部代码块创建方法没有意义

    代码块的执行顺序

    静态代码块 -> 构造代码块 -> 构造方法

    ===============================================================

    继承

    一个类和另一个类之间的关系,是一种什么是什么的关系(A is aB)

    如猪是动物,

    好处:子类继承父类可以直接使用父类开放(非private)的已存在成员

    image-20191119142825009

    使用

    extends关键字建立继承关系,一个类只能有一个父类

    class Animal{
      String name;
    }
    class Person extends Animal{
     public void show(){
       System.out.println(this.name);//直接访问父类成员
     }
    }
    

    重写/覆盖

    override

    什么是覆盖:

    ​ 当子类出现了与父类完全一致(相同返回值类型,相同名称,相同参数列表)的方法时将产生覆盖

    何时使用覆盖:

    ​ 当父类的方法 无法直接满足需求时子类可以覆盖父类已有方法

    注意:

    1.子类覆盖时方法的返回值类型必须与原始方法返回值类型一致,或者是原始方法返回值类型的子类
    2.子类覆盖方法时要求方法的权限必须大于等于原始方法的权限
    3.父类的静态成员不会被覆盖,当子类定义了与父类完全一致的方法时,这个方法实际是属于子类的与父类没有关系
    4.当子类出现方法名称与参数列表与父类方法一致时则认为是覆盖,要求返回值类型必须一致或是其子类

    也就是说如果子类想定义新的方法要么名称不同,要么参数列表不同

    补充:子类也可以定义与父类相同的属性,将覆盖父类相同的属性

    权限修饰符

    修饰符 本类 同包 子类 其他包
    public yes yes yes yes
    protected yes yes yes no
    default yes yes no no
    private yes no no no

    继承注意事项:构造方法无法被继承

    super

    当子类覆盖了父类的方法时,按照顺序将优先执行子类中的方法,当我们需要执行父类方法时

    可以在子类中使用super关键字来调用父类的方法

    class A{
      public A(){
     		   
      }
     	public show(){
        System.out.println("hello java!");
      }
    }
    
    class B extends B{
      public B(){
     		   super().show();
      }
    }
    
    注意:在子类的构造方法中系统会默认添加super(); 如果父类不存在空参构造函数将编译错误,我们也可以手动的使用super来调用其他的构造函数;
    强调:super()必须放在第一行

    对象的创建过程

    1.加载类(仅发生一次)

    2.加载父类静态资源

    3.加载子类静态资源

    4.执行父类构造代码块

    5.执行父类构造函数

    6.执行子类构造代码块

    7.执行子类构造函数

    8.完成

    this与super

    this:

    ​ 本类的,属性,方法,构造器

    super:

    ​ 父类的,属性,方法,构造器

    注意:this和super不能同时出现在构造函数中

    image-20191119212436052

    final

    翻译为最终,可以修饰类,变量,方法

    1.在继承关系中,被final修饰的方法无法被覆盖

    2.final修饰变量时,就变成了常量,一旦初始化值不允许修改

    3.final修饰类时,表示该类不能被继承

    注解

    JDK1.5 推出

    用于对方法或变量进行说明

    源码注解尽在编译完成时自动去除,用于对元素进行标记,方便编译器识别,例如@override

    编译时注解指的是编译成class后依然存在的注解

    运行时注解指的是,会对程序逻辑产生影响的注解,例如autowrite,transaction等..

    =============================================

    单例模式

    常见23中设计模式,及其分类;

    image-20191120005925881

    什么是单例:

    某个类有且仅有一个实例,那ta就是单例类

    image-20191120010309895

    java中的实现方式:

    1.将构造函数私有化 以禁止外界自己初始化

    2.利用static仅加载一次的特性,创建一个静态的对象作为类的私有属性

    3.提供访问这个静态对象的方法

    按照创建对象时机不同可以分为两种

    1.饿汉式: 直接在声明static属性时创建对象 这种方法是线程安全

    class SingleInstance{
      private static SingleInstance obj = new SingleInstance();
      private SingleInstance(){
        //私有化的构造函数 
      }
      public SingleInstance getInstance(){
        return obj;
      }
    }
    

    2.懒汉式:在获取对象是如果发现对象为空才创建 这种方法是非线程安全

     public class Emperor {
         //定义私有构造方法
        private Emperor(){
            System.out.println("create instance!");
        }
    	 //定义私有静态类对象
        private static Emperor obj = null;
    	 //定义公有静态方法返回类内的私有静态对象
    	 public static Emperor get_instance(){
    	     if(obj == null){
    	         obj = new Emperor();
    	     }
    	     return obj;
    	 }
    }
    
    

    优缺点:

    优点:

    ​ 节省空间,提高性能

    缺点:

    ​ 扩展困难

    ​ 当对象长期不使用,困难会被回收导致异常

    使用场景:

    当程序需要共享同一份对象的数据时

    当每个对象数据都相同时,则没有必要创建对个对象

    当需要保证某些数据的一致性时,例如: 要生成唯一的id,如果每个对象都有一份自己的生成方式则可能造成数据错误

    ================================

    多态

    定义:

    ​ 多个不同对象可以响应同一个方法产生各自不同的行为

    分类:

    1.编译时多态

    ​ 指的是在编译阶段就能识别具体要执行的方法是哪一个,通常是方法重载,通过参数不同决定调用哪个方法

    2.运行时多态

    ​ 只有在运行时才能确定到底调用哪个方法

    image-20191120095446497

    注意下面的内容都是针对运行时多态,是任何OOP语言的共同特性

    特点:

    出现多态的两个必要条件

    1.必须具备继承关系

    2.父类指针指向子类对象

    ps:在python中没有这两个限制,python是动态类型,编译期间不会检查对象具体类型,更加灵活,但是也增加了风险

    向上转型

    指的是用父类指针引用子类实例,会自动将子类转换为父类,转换后将隐藏子类特有的成员,只能使用父类中定义的方法和属性,

    向下转型

    当明确某个对象就是某个子类对象时可以强制转换为子类,如此就可以重新访问子类中独有的成员

    当不能明确对象是否是某个类型时可以使用 instanceof

    if(obj instanceof Objcet){
      System.out.println("obj is  ObjectClass instance");
    }else{
      System.out.println("obj not  ObjectClass instance");
    }
    
    注意:由于静态方法不能被重写,当子类和父类存在完全相同的静态方法时,向上转型后对象默认调用的是父类中的静态方法,也就是说不存在多态,调用的方法是明确的,跟随类型的

    abstract 抽象类

    什么是抽象类:

    抽象类指的是类中包含抽象方法的类,

    抽象方法指的是没有任何方法体的方法

    使用abstract关键字类表示抽象方法 或抽象类

    什么时候使用抽象类:

    当父类需要规定子类应该具备某些方法,但父类本身不清楚方法具体如何实现时使用抽象类,

    用于提前告诉子类,你应该具备哪些方法

    特点:

    存在抽象方法的类无法直接实例化产生对象,必须由子类继承并实现所有抽象方法才能实例化子类

    如子类没有实现所有抽象方法,那么子类也只能作为抽象类

    实例:

    abstract class Person{
    		 abstract public void show();
    }
    
    class Student extends Person{
      @Override
      public void show(){
        System.out.println("show");
      }
    }
    

    注意:

    1.如果设计子类的人清楚的知道自己应该做的事情时(方法),可以不用抽象类,抽象类本质就是限制子类必须怎么怎么滴

    2.需要注意final 不能与 abstract 同时出现

    interface 接口

    接口是一组功能(方法)的定义

    接口的作用:

    定义一组协议方法,让子类遵循协议中的要求去实现具体的操作,对于使用者而言,只需要掌握接口中定义的方法的使用即可,无需关心,具体是哪一个类实现的,更不用关心是如何实现的, 这将类的设计者和类的使用者之间的耦合度大大的降低了,

    定义语法:

    interface Name {
      
    }
    

    特点:

    接口本质是一个类,但是与普通类有着众多区别

    1.接口中的方法默认都是抽象的不能有方法体

    2.接口中定义的变量默认都是静态常量

    3.接口中的成员必然是public修饰的,即时没有明确声明

    4.接口无法直接实例化

    5.接口可以继承一个或多个其他接口

    6,一个类可以同时实现多个接口

    JDK1.8之后

    某些情况下,接口中声明了很多抽象方法,然而子类不需要实现全部,而是想仅实现部分需要的,这种情况在1.8之前是不允许的,除非把这个子类变成抽象的,这不够灵活;

    • 1.8之后,增加了新的方法修饰符,default,可以在接口中直接编写方法体,被default修饰的方法,子类可以选择性的重写
    • 新增特性,可以为方法加上static修饰符

    新特性的测试

    案例:

    package com.yh.src;
    
    interface HDMI {
        static int a = 0;
        // 上述代码等价于public static final int a = 0;
    
        //抽象方法
        void generalMethod();
        //上述代码等价于public void test();
    
        //默认方法 子类可选重写
        default void defaultMethod(){
            System.out.println("默认的方法体");
        }
        //静态方法 子类不可重写
        static void staticMethod(){
            System.out.println("静态方法体");
        }
    }
    
    class AA implements HDMI {
        @Override
        public void generalMethod() {
            System.out.println("AA Implements generalMethod!");
            HDMI.staticMethod();//静态方法只能有接口名称调用
        }
    
        @Override
        public void defaultMethod() {
            //super.test1();//无法通过super调用接口中的默认方法
            HDMI.super.defaultMethod();//需要使用接口名称.super来调用
        }
    }
    
    public class InterfaceRunner {
        public static void main(String[] args) {
            AA a = new AA();
            a.generalMethod();
            a.defaultMethod();//调用未被重写的默认方法
            //a.staticMethod();//无法直接调用接口中的静态方法
            HDMI.staticMethod();//静态方法只能由接口名称调用
    
        }
    }
    
    
    default方法总结:
    • default方法子类可以选择是否重写,调用顺序与普通继承相同,优先调用对象重写的方法,
    • 当需要在重写方法中调用接口中的默认实现时使用接口名称.super.方法名称HDMI.super.defaultMethod();
    static方法总结:
    • static方法无法被重写,也不能使用super调用,无论在什么位置只能使用接口名称来调用

    PS:其实取消抽象方法和接口一样可以实现设计-使用松耦合,直接使用普通类作为父类,子类自己实现该实现的方法,当然了java为什么占领企业开发也正是因为其严谨,标准规范,

    你会发现,为了提高灵活性接口好像变得和抽象类非常相似,.......干脆像python一样别整这么多约束,,,

    接口多实现

    与普通类不同的是一个类可以同时实现多个接口,增加了子类的扩展性

    但是也带来了访问不确定性,

    • 情况1:多个接口存在相同的方法(名称与参数列表相同)
    interface IA{
        default void test(){
            System.out.println("IA test!");
        }
    }
    
    interface IB{
        default void test(){
            System.out.println("IB test!");
        }
    }
    //实现类直接编译失败,
    class IMA implements IA,IB{
    
    }
    
    解决方案:
    //在子类中重写冲突的方法,即可 当需要调用默认方法时,使用`接口名称.super.xx`来调用
    class IMA implements IA,IB{
    
        @Override
        public void test() {
            IA.super.test();
            IB.super.test();
        }
    }
    
    • 情况2:父类中存在相同的方法
      interface IA{
          default void test(){
              System.out.println("IA test!");
          }
      }
      
      interface IB{
          default void test(){
              System.out.println("IB test!");
          }
      }
      
      class P{
          public void test(){
              System.out.println("IB test!");
          }
      }
      
      class IMA extends P implements IA,IB{
      }
    

    ​ 这种情况下不会出现问题,将直接调用父类中的方法

    • 情况3:多个接口中出现了重复的常量
    interface IA{
        int a =10;
        int b =100;
    }
    
    interface IB{
        int a =20;
    }
    
    class P{
        int a = 20000;
    }
    
    class IMA extends P implements IA,IB{
        public void  func(){
            System.out.println(a);//编译错误
            System.out.println(IA.a);//使用接口名称来明确
            System.out.println(super.a);//使super来指定访问父类
            System.out.println(b);//不存在重复的时直接可以访问
            
        }
    }
    

    此时子类无法明确选择一个要访问的常量,必须使用接口名称来明确

    要访问父类时使用super来明确

    补充:

    子类对象可以直接访问接口中没有冲突的静态变量,但是无法直接访问静态方法需要使用接口名称调用

    ===================================

    内部类

    什么是内部类:

    当一个类定义在一个类的范围中,则这个类称之为内部类,与之对应的是包含这个内部类的外部类

    普通内部类(较少用)

    实例:

    class A{//外部类
      class B{ //内部类
        }
    }
    

    实例化内部类

    public class InnerClass {
        public static void main(String[] args) {
            //实例化内部类方法1
            A.B obj; //定义
            obj = new A().new B();
            //实例化内部类方法2
            A aobj = new A();
            obj = aobj.getInner();
            //实例化内部类方法3
            obj = aobj.new B();
        }
    }
    class A{
        public B getInner() {
            return new B();
        }
        class B{
        }
    }
    

    内部类访问外部类的成员

    public class InnerClass {
        public static void main(String[] args) {
            //实例化内部类方法1
            A.B obj; //定义
            obj = new A().new B();//实例化
            obj.test();//调用方法访问外部类的成员
        }
    }
    class A{
        int inta = 100;
        int intb = 200;
        public B getInner() {
            return new B();
        }
        class B{
            int intb = 300;
            public void test(){
                System.out.println(inta);//100 可以直接访问外部类成员
                System.out.println(intb);//300 冲突时优先访问内部
                System.out.println(A.this.intb);//200 冲突时指定访问外部
            }
        }
    }
    

    外部类访问内部类成员

    class A{
        int inta = 100;
        int intb = 200;
        public void accessInner() {
            B b = new B();//实例化
            b.test();//访问
        }
    
        class B{
            int intb = 300;
            public void test(){
                System.out.println(inta);
                System.out.println(intb);
                System.out.println(A.this.intb);
            }
        }
    }
    

    外部类访问内部类成员时需要先实例化内部类的对象,通过对象访问即可

    注意:普通内部类中不能包含任何static修饰的成员

    静态内部类 (不常用)

    可以将内部类使用static修饰,此时他就是静态内部类

    特点:

    • 与普通内部类的不同之处,与static修饰的其他成员有着相同的特征:

    • 在类被加载时就一起加载,所以,可以直接实例化,不需要先实例化外部类;

    • 同样的在静态内部类中不可以直接访问非静态修饰的外部类成员,也不可以访问外部类的this;

    • 静态内部类中的可以包含静态成员并且也可以直接调用(外部类.内部类.属性);普通类中不可以有静态成员;

    实例:

    public class InnerClass2 {
        public static void main(String[] args) {
            System.out.println(OUT.IN.a);//直接访问静态内部类的静态成员
            OUT.IN.test();//访问静态内部类方法
        }
    }
    
    class OUT{
        static int a = 10;
        String name = "jack";
        static class IN{
            static int a = 100;
            static public void test(){
                System.out.println(a);//优先访问内部类
                System.out.println(OUT.a);//指定访问外部类
                System.out.println(new OUT().name);//访问外部非静态成员
            }
        }
    }
    

    局部内部类 (不常用)

    定义在方法中的类称为局部内部类

    意义不大..没啥用..略过了

    class A{
    	public void test(){
        class B{
          //局部内部类
    		}
      }
    }
    

    特点:不允许使用static修饰局部内部类,和其中的任何成员,要使用该类必须作为返回值return出去

    匿名内部类 (很常用)

    什么是匿名内部类

    字面意思就是没有名字的类

    通过new 实例化接口或是抽象类,同时提供相应的方法实现

    实例:

    interface USB{
      void open();
    }
    
    class PC {
    	public void working(){
    		//此处需要一个实现了USB接口的对象来完成操作
        //我们可以定义一个类实现USB接口先下面的A类一样
        //然后实例化A对象来完成OPEN操作
        new A().open();
    		//我们也可以使用匿名内部类的方式来简化操作 
        new USB(){
    			  public void open(){
        		System.out.println("OPEN......");
    	  	}
        }.open;
      }
    }
    class A implements USB{
      public void open(){
        System.out.println("OPEN......");
      }
    }
    

    在上面的例子中我们可以看出匿名内部类有以下优点:

    • 代码更加紧凑

    • 使用更便捷

    当然匿名内部类的缺点也很明显:

    • 这个类产生的对象也是没有名字的,只能在定义的地方使用一次
    • 语法格式看起来比较乱

    什么时候使用匿名内部类

    1.临时需要某个接口或是抽象类的独享完成某个功能

    2.当整个任务的部分代码已经完成,但是剩下一部分关键代码需要由使用这个功能的人来完成

    案例:

    PC类的working方法需要传入一个USB接口对象才能完成整个任务

    interface USB{
        void open();
    }
    public class PC {
        public static void main(String[] args) {
            new PC().working(new AP());//以前的方式:定义类实现方法然后实例化
            new PC().working(new USB() {//匿名内部类的方式:在当前位置完成上述全部操作
                @Override
                public void open() {
                    System.out.println("open on NonNameClass");
                }
            });
        }
        public void working(USB usb){
            usb.open();
        }
    }
    class AP implements USB{
        public void open(){
            System.out.println("OPEN......on AP");
        }
    }
    

    PS:个人觉得匿名内部类+匿名对象其实 与OC中的block,python中的函数对象要完成的事情是一样的,即方法的使用者实现部分代码,然后作为参数传给某个方法(本质就是回调机制),但是JAVA中定义方法必须借助类,所以才有了匿名内部类这么一说,归根结底是因为JAVA强制面向对象导致的;

    补充:匿名内部类中可以使用构造代码块

    代码设计规范----单一功能职责:

    有且只有一个引起功能变化的原因
    如果在一个类当中,承担的职责越多,耦合度越高,这意味着这个类的重用性越低
  • 相关阅读:
    由u盘安装Ubuntu引出的事件
    初试Ubuntu
    从error 中学习
    快手一面:牛客:字符串左移
    快手一面:Leetcode:最小栈
    十三、线程池
    十二、windows临界区、其他各种mutex互斥量
    十一、std::async深入
    LeetCode(703):找出数据流中的第K大元素
    LeetCode(1003):检查替换后的字符串
  • 原文地址:https://www.cnblogs.com/yangyuanhu/p/11901718.html
Copyright © 2020-2023  润新知