zoukankan      html  css  js  c++  java
  • 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强制面向对象导致的;

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

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

    有且只有一个引起功能变化的原因
    如果在一个类当中,承担的职责越多,耦合度越高,这意味着这个类的重用性越低
  • 相关阅读:
    通讯录封装实现
    简单通讯录的实现 main..h .m文件全部
    iOS 开发 OC编程 字典和集合 排序方法
    iOS 开发 OC编程 数组冒泡排序.图书管理
    iOS 开发 OC编程 属性和字符串练习
    iOS 开发 OC编程 属性和字符串
    iOS 开发 OC编程 便利构造器以及初始化方法
    iOS 开发 OC编程 方法的书写
    IOS 开发 OC编程 类和对象
    iOS 开发 c语言阶段考试题
  • 原文地址:https://www.cnblogs.com/yangyuanhu/p/11901718.html
Copyright © 2011-2022 走看看