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外部类的变量
原文地址:https://www.cnblogs.com/tianwenxin/p/14746387.html