• java笔记八——面向对象(三)


    一、静态修饰符:static

    static修饰符 : 最早出现在main方法中。只能修饰成员,不能写在方法的内部。

    被static修饰的成员:静态成员变量和静态的成员方法。

    静态修饰成员变量

    static 修饰的成员变量,是被所有的对象共享的数据。没有被static修饰的成员变量,是每个对象的独享数据或者是特有数据。

    public class Person {
        String name;
        static String country = "中国";
    }
    
    public class StaticTest {
        public static void main(String[] args) {
            Person p1 = new Person();
            p1.name = "张三";
            Person p2 = new Person();
            p2.name = "李四";
            //使用对象p1修改变量country的值
            p1.country = "美国";
            System.out.println(p2.country);
        }
    }
    

    静态内存

    静态成员内存的特点

    • 静态成员跟随自己的类进入到元数据区(静态区域)
    • 静态成员属于自己的类,不属于对象
    • 静态成员进入内存后,赋默认值
    • 静态成员变量的初始化实际早于对象

    静态成员的调用方式

    静态的内存图中,已经很明白了,静态属于自己的类,不是对象,静态的调用方式应该是类名.静态成员

    Person.country ;//调用静态成员
    
    public static void main(String[] args) {
        System.out.println(Person.country);
        Person p1 = new Person();
        p1.name = "张三";
        Person p2 = new Person();
        p2.name = "李四";
        //使用对象p1修改变量country的值
        Person.country = "美国";
        System.out.println(Person.country);
    }
    

    静态的调用方式是两种 : 类名调用,对象调用.。非静态成员调用只能对象。

    静态成员调用方式只有类名.调用 ,非静态成员只能对象.调用。 对象.静态调用方式,会被javac编译为类名调用。

    静态方法

    ① 静态方法调用格式:类名.方法名

    ② 静态方法中不能直接使用非静态成员。

    为什么不能调用非静态成员?

    静态是先人,非静态是后人。 静态出现在内存的时间早于非静态。

    public class Person {
        String name;
        static String country = "中国";
    
        /**
         * 静态方法
         */
        public static void eat(){
            System.out.println("人在吃饭" + country);
            System.out.println(name); //错误写法,不允许
        }
    }
    

    静态内存优先于对象,在静态的方法中不能使用this和super

    main方法

    public static void main(String[] args){
        
    }
    

    main方法详解

    • public 最大权限 : main方法的调用者是JVM
    • static 无需对象,被类名直接调用,JVM启动的时候使用类名.main启动程序
    • void 无返回值,调用者是JVM,方法的返回值都是返回给调用者,JVM不需要返回值,没有意义
    • main 固定方法名称
    • args 字符串的数组,JVM调用方法main必须传递参数,后期对JVM设置参数

    二、final修饰符

    final修饰符是最终的意思,不可改变。final可以修饰类,修饰方法,修饰成员变量,修饰局部变量。

    final修饰类

    被final修饰的类,称为最终类,不能被其他的类继承。无子类、 太监类。

    学过的final类有哪些, String,System,Scanner。

    public final class A{} //这个类A,不能出现子类,如果继承,直接报错
    public class B extends A{} //错误,编译错误,最终类不能继承
    

    final修饰方法

    被final修饰的方法是最终方法,不能被子类重写。可以被调用。

    一个类中的部分方法很完美,但是另一部分方法有待完成,设计为两个部分。完美的方法就是final 。

    public class A{
        public final void a(){} //方法不能被子类重写
    }
    public class B extends A{
        public void a(){} //最终方法,不能重写
    }
    

    final修饰局部变量

    变量定义在方法的内部,是局部变量, final修饰后,一次赋值,终身不改变,锁死了变量的值,可以看做是常量。

    • final修饰的基本类型,锁死值
    • final修饰的引用类型,锁死内存地址 (引用类型中的成员不受影响)
    • final修饰了方法的参数,调用者传递值后,方法的参数值就锁死
    public static void main(String[] args) {
        /**
        *   Student student 对象存储的是内存地址
        *   final修饰后,固定住的,不可改变是student变量保存的地址
        *   但是,Student对象中的成员,不受影响
        */
        final  Student student = new Student();
        student.age = 20;
        student.age = 30;
        System.out.println(student.age);
    
        final int[] arr = {1,2,3};//arr变量的值,固定为内存地址,不可改变
        arr[1] = 200;
    
        show(5);
        }
    
        public static void show(final int x){
        	x = 6; //final修饰,不可改变,报错
        }
    }
    

    final修饰成员变量

    成员变量的定义位置是在类中、方法外面。成员变量在内存中有默认值。final修饰成员变量的时候,锁住的不是内存默认值,而是我们程序人员手动的赋值。(如果我们不手动赋值,IDEA会提示错误)

    • final修饰成员变量,可以直接手动赋值:int age = 0;

    • final修饰成员变量,可以使用构造方法:public Student(int age){this.age=age;}

    • final修改成员变量,不可以使用set方法完成

      • final修饰的成员变量,可以构造方法赋值,不能set方法赋值
      • 构造方法在new对象的时候,执行一次,仅仅一次
      • 可set方法,可以反复执行!!
    public class Student {
        final int age ;
    
        public Student (int age ){
            this.age = age;
        }
    
       /*public void setAge(int age){
           this.age = age;	// 编译报错
       }*/
    }
    

    三、代码块

    静态代码块

    写在类中方法外面 : static{}

    静态代码块的执行时机 : 只要使用了这个类的成员(new对象,调用静态方法,静态变量),静态代码块就会执行,而且就一次

    静态代码块主要用于 : 数据库连接池 (C3P0,Druid)

    JDBC注册数据库驱动程序,使用静态代码块

    构造代码块

    写在类中方法外面的 {}, 创建对象的时候运行,new一次,运行一次

    局部代码块

    写在方法内部的 {} 局部代码块,没有用

    对象的初始化过程(父子类)

    • 父类.class文件先进入内存
    • 子类.class文件再进入内存
    • 初始化父类的静态成员(变量,代码块,方法)
    • 初始化子类的静态成员
    • 运行父类的静态代码块
    • 运行子类的静态代码块
    • 运行父类的构造代码块
    • 运行父类的构造方法
    • 运行子类的构造代码块
    • 运行子类的构造方法
    public class Person {
        static {
            System.out.println("1.父类的静态方法");
        }
    
        public Person() {
            System.out.println("4.父类的构造方法");
        }
    
        {
            System.out.println("3.父类的构造代码块");
        }
    }
    
    public class Student extends Person{
        static {
            System.out.println("2.子类的静态方法");
        }
    
        public Student(){
            System.out.println("6.子类的构造方法");
        }
    
        {
            System.out.println("5.子类的构造代码块");
        }
    }
    
    public static void main(String[] args) {
            new Student();
        }
    }
    

    三、内部类

    含义

    在Java编程语言里,程序是由类(class)构建而成的。在一个类的内部也可以声明类,我们把这样的类叫做内部类。

    例如:

    class A{ //外部类,封闭类
        class B{} //内部类,嵌套类
    }
    

    作用

    • 实现了更好的封装,我们知道,普通类(非内部类)的访问修饰符不能为private或protected,而内部类可以。当我们将内部类声明为private时,只有外部类可以访问内部类,很好地隐藏了内部类。
    • 内部类可以继承(extends)或实现(implements)其他的类或接口,而不受外部类的影响。
    • 内部类可以直接访问外部类的字段和方法,即使是用private修饰的,相反的,外部类不能直接访问内部类的成员。

    原理

    内部类是一个编译时的概念,编译后会生成两个独立的class文件,如下:

    public class Outer{
          private String outerName = "outer";
          class Inner{
              private String innerName = "inner";
          }
      }
    

    编译后生成的文件如下图:

    编译后Outer.Inner被重命名为Outer$Inner,句点(.)被替换成了美元符号($)。

    分类

     Java内部类可分为成员内部类、局部内部类、匿名内部类、静态内部类。

    成员内部类

    ① 成员内部类特点:

    • 成员内部类可以看成是外部类的一个成员

    • 在成员内部类中无法声明静态成员,但可以声明 static final int y = 5;

    ② 成员内部类中不能声明静态成员的原因:

    ​ 我们知道加载类时,会先初始化静态成员,如果成员内部类有静态成员,那么内部类就会在外部类之前生成,而内部类是为外部类服务的,内部类在外部类之前就生成可能会脱离掌控。在实例化成员内部类时,成员内部类会持有一个外部类当前对象的引用,这样在成员内部类中就可以直接访问外部类的成员,即使是private修饰的。

    ③ 成员内部类的调用方式

    公式 : 外部类名.内部类名 变量名 = new 外部类对象().new 内部类对象()

    ④ 举例:

    public class Outer {
        private String outerName = "outer";
       // 外部类无法直接访问内部类的成员,需要实例化内部类对象
        private Inner inner = new Inner();
    
        // 定义内部类
        public  class Inner {
            private String innerName = "inner";
            public void show(){
                System.out.println("内部类可以直接访问外部类的成员包括私有属性:" + outerName);
            }
        }
    
        // 访问内部类中的方法
        public void show(){
            System.out.println("外部类需要先创建内部类对象才能访问内部类成员:" + inner.innerName);
        }
    }
    
    public static void main(String[] args) {
            // 创建外部类Outer的对象,借助Outer类中间接访问Inner类
            Outer outer = new Outer();
            outer.show();
            // 直接访问Inner类中的方法
           Outer.Inner inner = new Outer().new Inner();
           inner.show();
        }
    

    运行结果:

    外部类需要先创建内部类对象才能访问内部类成员:inner内部类可以直接访问外部类的成员包括私有属性:outer
    

    局部内部类

    ① 局部内部类特点

    • 局部内部类的使用和成员内部类的使用基本一致,只是局部内部类定义在外部类的方法中,就像局部变量一样,并不是外部类的成员。
    • 局部内部类在方法外是无法访问到的,但它的实例可以从方法中返回,并且实例再不被引用之前会一直存在。
    • 局部内部类也可以访问所在方法的局部变量、方法参数等,限制是局部变量或方法参数只有在声明为final时才能被访问。

    ② 举例:

    public class Outer {
        private String outerName = "outer";
    
        public void show(final String str){   // 方法参数为final型
            class Inner{
                public void print(){
                    System.out.println(outerName + str);
                }
            }
            // 创建内部类对象
            Inner inner = new Inner();
            inner.print();
        }
    
    
     public static void main(String[] args) {
            // 创建外部类Outer的对象,借助Outer类中间接访问Inner类
            Outer outer = new Outer();
            outer.show(":lalala");
    
        }
    

    运行结果:

     outer:lalala 
    

    匿名内部类

    ① 可以把匿名内部类想象成是没有类名的局部内部类,匿名内部类有以下特点:

       1、匿名内部类不能有构造器,匿名内部类没有类名,肯定无法声明构造器。

       2、匿名内部类必须继承或实现一个接口,指定给new的类型为匿名类的超类型,

    ​ 3、匿名类不能有显示的extends或implements子句,也不能有任何修饰符。

       4、匿名内部类和成员内部类、局部内部类一样,也不能声明静态成员。  

    ② 格式:

    new 接口或者父类(){
        //重写抽象方法
    };
    

    ③ 例子:

    public interface Inner {
        public abstract void show();
        public abstract void print();
    }
    
    public static void main(String[] args) {
            // 创建匿名内部类并用多态接收对象
            Inner inner = new Inner() {
                // 重写方法
                @Override
                public void show() {
                    System.out.println("重写的第一个方法");
                }
    
                @Override
                public void print() {
                    System.out.println("重写的第二个方法");
                }
            };
            // 调用重写后的方法
            inner.show();
            inner.print();
        }
    

    运行结果:

    重写的第一个方法重写的第二个方法
    

    静态内部类

    ① 静态内部类特点:

    ​ 1、静态内部类位于类中方法外。和成员内部类的位置相同

    ​ 2、静态内部类不同于前三种内部类,静态内部类不会持有外部类当前对象的引用,所以在静态内部类中无法访问外部类的非静态成员,可以这么说,静态内部类不依赖于外部类。

    ② 静态内部类的调用方式:

    公式:外部类名.内部类名 变量名 = new 外部类.内部类()

    ③ 举例:

    public class Outer {
        private String outerName = "outer";
        private static int id = 123;
        private Inner inner = new Inner();
    
        // 定义内部类
        public static class Inner {
            public void print1() {
    //            System.out.println(outerName);  // 无法访问外部类的非静态成员
                System.out.println(id);
            }
            public static void print2(){
                System.out.println(id);
            }
        }
    
        // 访问内部类中的方法
        public void show(){
            inner.print1();
        }
    }
    
     public static void main(String[] args) {
            // 创建外部类Outer的对象,借助Outer类中间接访问Inner类
            Outer outer = new Outer();
            outer.show();
            // 直接访问Inner类中的方法
            Outer.Inner.print2();
        }
    

    运行结果:

    123
    123
    

    内部类中出现相同的变量名

    ①成员内部类举例

    public class Outer {
         String str = "外部类的变量";
    
        class Inner{
            String str ="内部类的变量";
            public void show(){
                String str = "内部类中方法的变量";
                System.out.println("str = " + str);
                System.out.println("this.str = " + this.str);
                System.out.println("Outer.this.str" + Outer.this.str);
            }
        }
    }
    
     public static void main(String[] args) {
            
            // 直接访问Inner类中的方法
           Outer.Inner inner = new Outer().new Inner();
           inner.show();
        }
    

    运行结果:

    str = 内部类中方法的变量
    
    this.str = 内部类的变量
    
    Outer.this.str外部类的变量
    
  • 相关阅读:
    观察者模式-Observer
    @Resource、@Autowired、@Qualifier的注解注入及区别
    Java垃圾回收(GC)机制详解
    java什么时候进行垃圾回收,垃圾回收的执行流程
    Log4J.xml配置详解
    springMVC配置拦截器、过滤器、前端控制器时遇到的问题总结
    基于zookeeper实现分布式锁
    数据库索引原理及优化
    数据分析——CentOS7配置时间和ntp
    solr——zookeeper部署
  • 原文地址:https://www.cnblogs.com/tianwenxin/p/14746387.html
Copyright © 2020-2023  润新知