JAVA类及类的成员

类的简介:

一个源文件*.java必须与该文件中的主类同名(public class),源文件中有且只有一个主类。-->主方法main-存在与主类中;一个源文件中可以存在N个普通类,直接使用。

类中常量:static、final同时修饰,此时常量全局唯一且不可修改,所有对象共享此常量

阿里编码规约:类中如无特殊说明,所有成员变量统一使用private 封装(99%)

1.属性

属性 = 成员变量 = field = 域、字段

                                  属性  VS  局部变量

1.相同点:

1.1 定义变量的格式:数据类型   变量名  =  变量值

1.2 先声明,后使用

1.3 变量都有对应的作用域

2.不同点:

2.1 在类中声明的位置的不同

属性:直接定义在类的一对{}内

局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量

2.2 关于权限修饰符的不同

属性:可以在声明属性时,指明其权限,使用权限修饰符。

           常用的权限修饰符:private、public 、缺省、protected  ---->封装性

局部变量不可以使用权限修饰符。

2.3默认初始化值的情况:

属性:类的属性,根据其类型,都默认初始化值。

        整型 (byte、short、int 、long: 0)

        浮点型 (float、double: 0.0)

        字符型 (char: 0   (或'\u0000'))

        布尔型 (boolean: false)

        引用数据类型(类、数组、接口:null) 

JAVA类及类的成员_第1张图片

局部变量:没默认初始化值。

意味着,我们在调用局部变量之前,一定要显示赋值。

        特别地,形参在调用时,我们赋值即可。

2.4 在内存中加载的位置:

属性:加载到堆空间中 (非static)

局部变量:加载到栈空间

3.属性赋值的先后顺序:

A.默认初始化 B.显示初始化  C.构造器中赋值  D.通过“对象.方法” 或 “对象.属性”的方式,赋值

以上操作的先后顺序:A -> B -> C -> D

所有变量:

1.成员变量:(类中的成员变量都在堆上保存)。

1.1 实例变量(不以static修饰):A.任何方法都可以调用,B.类加载过程中完成静态变量的内存分配,只分配一次内存,且在内存中只有一个备份,静态变量一改全改,C.通过类名可以直接调用静态变量

1.2 类变量(以static修饰):A.静态方法不能使用非静态的成员变量,只有实例化对象之后,才能调用;B.每创建一个实例,JVM就会为实例变量分配一次内存,可在内存中有多个拷贝,互不影响

1.2 局部变量

1.2.1 形参(方法、构造器中定义的变量)1.2.2 方法局部变量(在方法内定义)

1.2.3 代码块局部变量(在代码块内定义)

注:局部变量的初始值必须被显式声明!(局部变量没有默认初始化值)

变量的作用域

1.静态成员变量:类加载时创建,与类同命(静态属性保存在JVM的方法区

2.成员变量:实例被创建时与实例对象同命

3.局部变量:在定义该变量的方法被调用时创建,与方法同命

2.方法

1.方法的基本描述

方法 = 成员方法 = 函数 = method

方法:描述类应该具有的功能。

比如:Math类:sqrt()\random()\...

           Scanner类:nextXxx() ...

           Arrays类:sort() \ binarySearch() \ toString() \ equals() \ ....

1.举例:

public void eat(){}
public void sleep(int hour){}
public String getName(){}
public String getNation(String nation){}

2.方法的声明:权限修饰符  返回值类型   方法名(形参列表){

                        方法体

                        }

3.说明:

        3.1 关于权限修饰符:默认方法的权限修饰符都先使用public

              Java规定的4种权限修饰符:private、public 、缺省、protected---->封装性的体现

        3.2 返回值类型: 返回值  vs  没返回值

               3.2.1 如果方法返回值,则必须在方法声明时,指定返回值的类型。同时,方法中,需要                          使用return关键字来返回指定类型的变量或常量:“return 数据”。

                        如果方法没有返回值,则方法声明时,使用void来表示,通常,没返回值的方法中,使用return,但是,此时,只能:“return ;” 表示结束此方法的意思。

        3.3 方法名:属于标识符,遵循标识符的规则和规范,“见名知意”

        3.4 形参列表:方法可以声明0个,1个,或多个形参。

              3.4.1格式:数据类型1 形参1,数据类型2 形参2,...

        3.5 方法体:方法功能的体现。

        方法的使用中,可以调用当前类的属性或方法。

        特殊的:方法A中又调用了方法A:递归方法。

        方法中,不可以定义方法。

4.方法的形参的值传递机制:值传递

        所谓的值传递,就是将实参值的副本传入方法里,而参数本身并不受影响。

        形参:方法定义时,声明的小括号内的参数

        实参:方法调用时,实际传递给形参的数据

        值传递机制:

        如果参数时基本数据类型,此时实参赋给形参的时是实参真实存储的数据值。

        如果参数时引用数据类型,此时实参赋给形参的是实参存储数据的地址值。

2.方法的重载(overload)

1.定义:在同一个类中,允许存在一个以上的同名方法,只要他们的参数个数或者参数类型不同即可。

两同一不同”:同一个类、相同方法名

                       参数列表不同:参数个数不同,参数类型不同

2.举例:

Arrays类中重载的sort() / binarySearch()

3.判断是否重载:

        跟方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系!

4.在通过对象调用方法时,如何确定某一个指定的方法 :

        方法名------>参数列表

3.方法的重写

4.区分方法的重载和重写

重载:不表现为多态性。

重写:表现为多态性

1.从编译和运行的角度来看:

重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数列表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。他们的调用地址在编译期间就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。

所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;

而对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”;

引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,它就不是多态。”

5.方法的调用

1.非静态方法

非静态的方法就是没有static修饰的方法,对于非静态方法的调用,是通过对象来调用的

对象.方法()
public class InvokeMethod{
    public static void main(String[] args){
        InvokeMethod in = new InvokeMethod();
        in.t1();
    }
    public void t1(){

    }
}

2.静态方法

静态方法就是用static修饰的方法,静态的方法的调用是通过类名来调用的

类名.方法()
public class InvokeMethod{
    public static void main(String[] args){
        InvokeMethod.t2();
    }
    public static void t2(){

    }
}

3.方法与方法之间的调用

3.1静态方法内部调用其他方法

     如果在本类当中,静态方法可以直接调用静态方法,除了在main方法中,还可以在自定义的静态方法中直接调用。如果在本类当中是非静态方法,就必须通过对象来调用。

public class InvokeMethod{
    public static void main(String[] args){
       t2();
    }
    public static void t2(){
        system.out.println("static t2...");
    }
    public static void t1(){
    //静态方法调用非静态方法需通过对象来调用
    //InvokeMethod in = new InvokeMethod();
    //in.t2();
    t2();
    system.out.println("static t1");
    }
}

     如果在不同类当中,静态方法调用其他类的非静态方法,需要导入该类中的包,以及通过创建对象调用。

3.2非静态方法内部调用

     如果在本类中,非静态方法可以直接调用静态方法与非静态方法。

     在不同类中,非静态方法调用其他类的静态方法,需要通过导入该类中的包,并且需要通过类名来调用。

     在不同类中,非静态方法调用其他类的非静态方法时,需要导入该类中的包,还需要通过创建对象来掉用。

3.构造器

注:构造器名必须与本类的public访问修饰符的类名相同。

1.构造器的作用:

1.1 创建对象    1.2  初始化对象的信息

构造方法是执行对象的初始化操作(初始化:类中成员变量赋值)

构造方法就是给类中属性赋值,类中属性的类型,在定义时就规定好了。

2.说明:

2.1 如果没有显示的定义类的构造器的话,则系统默认提供一个空参的构造器

2.2 定义构造器的格式:权限修饰符 类名(形参列表){}

2.3 一个类中定义的多个构造器,彼此构成重载

2.4 一旦我们显示的定义了类的构造器之后,系统就不再提供默认的空参构造器

2.5 一个类中,至少会有一个构造器。

2.6 构造方法的互相调用:【使用this(相应参数),必须放在该方法的第一行调用,并且调用不能成"环",不能递归调用成环】

关于构造器的特殊点:(private修饰构造方法)A. 产生对象时,由JVM调用 B. 一旦构造方法被private修饰,这个类就不能通过外部产生对象;举例:对象个数有限,类内部提供方法产生对象,当一个类构造方法被private修饰,表示不希望它能通过外部产生对象,二由类本身向外提供对象,外部只能使用,不能创建。eg:星期类(周一 ~ 周天这七个对象)、地球类,只有一个对象。

public class Test1 {
    public static void main(String[] args) {
        Person person = Person.getInstance();
    }
}
class Person{
    private int age;
    private String name;
    private static Person per = new Person();
    private Person(){
        //类内部提供好对象,供外部使用
        public static Person getInstance(){
            return per;
        }
    }
}

4.代码块 


 1.代码块的作用:用来初始化类、对象
 2.代码块如果有修饰的话,只能用static.
 3.分类:静态代码块  vs 非静态代码块
3.1静态代码块
 >内部可以有输出语句
 >随着类的加载而执行,而且只执行一次
 >作用:初始化类的信息
 >如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行
 >静态代码块的执行要优于非静态代码块的执行
 >静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构

 3.2非静态代码块
 >内部可以有输出语句
 >随着对象的创建而执行
 >每创建一个对象,就执行一次非静态代码块

 >作用:可以在创建对象时,对对象的属性等进行初始化
 >如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行
 >非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法
 对属性可以赋值的位置: (1)默认初始化  (2)显示初始化   (3)构造器中初始化    (4)有了对象以后,可以通过“对象.属性”或“对象.方法”的方式,进行赋值   (5)在代码块中赋值
 执行的先后顺序:(1)->(2)/(5)->(3)->(4)            静态代码块 > 动态初始化块 > 构造器

关于代码块的另外一个分类:

A.普通代码块:定义在方法中的代码块【知道即可】

B.构造块:定义在类中,不加任何修饰符,直接使用{}定义的代码块

当产生对象时执行构造代码块,优先于构造方法执行。有几个对象产生,构造代码块就执行几次。

C.静态代码块:一般用于初始化静态成员属性。

定义在类中,使用static修饰的代码块。当类加载时执行一次,与对象产生无关。

静态代码块不管生成多少个对象,其只会执行一次,且是最先执行的。

静态代码块执行完毕后,构造代码块(执行),在然后是构造方法执行。

D.同步代码块:(多线程)

class Person {
	String name;
	int age;
	static String desc = "我是一个人";
	public Person() {
	}
	public Person(String name, int age) {
		this.age = age;
		this.name = name;
	}
	// static代码块
	static {
		System.out.println("hello,static Block->1");
		System.out.println("我是一个爱学习的人");
	}
	static {
		System.out.println("hello,static Block->2");
		// 调用静态结构
		desc = "我是一个爱学习的人";

		// 不可以调用非静态结构
//		name = "Tom";
	}
	// 非static代码块
	{
		System.out.println("hello,Block->1");
		System.out.println("hello,Block->2");
		// 调用非静态结构
		age = 1;
		// 调用静态结构
		desc = "我是一个爱学习的人1";
	}
}

阿里笔试题:

public class D extends B{
    public D(){
        System.out.println("4.D的构造方法");
    }
    {
        System.out.println("5.D的构造块");
    }
    static{
        System.out.println("6.D的静态代码块");
    }

    /**
     * 此时主方法所在的类D有静态代码块
     * 在执行主方法之前先加载类(先执行静态代码块)
     *  -> 先加载B而后加载D
     * new D() -> 产生子类对象
     * 先调用父类的构造方法产生父类对象
     * @param args
     */
    public static void main(String[] args) {
        System.out.println("7.............");
        new D();
        new D();
        System.out.println("8.............");
        /*
        执行结果:3.B的静态块
        6.D的静态代码块
        7.............
        2.B的构造块
        1.B的无参构造    
        5.D的构造块
        4.D的构造方法
        2.B的构造块
        1.B的无参构造
        5.D的构造块
        4.D的构造方法
        8.............
         */
    }
}
class B{
    public B(){
        System.out.println("1.B的无参构造");
    }
    {
        System.out.println("2.B的构造块");
    }
    static{
        System.out.println("3.B的静态块");
    }
}

5.内部类

Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类
内部类的分类:

成员内部类(静态、非静态) vs 局部内部类(方法内、代码块内、构造器内)

A.成员内部类:直接定义在类中,不加static定义的内部类

成员内部类必须依赖于外部类的存在而存在,必须先有外部类对象才能产生成员内部类。

//成员内部类对象的产生
Inner in = new Inner();

//外部类的外部:前提是内部类对于外部的这个类可见的
Outter.Inner in = new Outter().new Inner();
//new Outter():产生了外部类的对象,然后通过此对象来构造内部类对象 => 当产生内部类对象时,构造这个内部类的外部类对象就会被编译器传入内部类

成员内部类实际上JVM会传入一个隐藏的外部类对象Outter.this 

        Outter.Inner1 inner = new Outter().new Inner();
        //外部类Outter的对象在产生内部类的时候就由JVM传递给内部类
        System.out.println(Outter.this.age);

内部类和外部类可以方便的访问彼此的私有域

JAVA类及类的成员_第2张图片

JAVA类及类的成员_第3张图片 无论那种内部类,在外部类的内部使用内部类的成员方法和成员属性,都必须显式的通过内部类对象来访问。反之,对于成员内部类来说,成员内部类先有外部类对象后才能创建成员内部类对象。

Outter.Inner in = new Outter().newInner();

内部类访问外部类的私有域直接访问

外部类要访问内部类的私有域,必须通过对象来访问。

class Outter{
    private String msg = "outter类中的msg属性";
    //心脏和发动机都属于私有内部类,对外部完全隐藏,只是在类的内部来使用
    
    class Inner{
        private int num = 10;
        private String msg = "内部类的msg属性";
        public void test(){
            //直接访问外部类的msg属性
            //此处直接访问了外部类的私有成员变量msg,成员变量必须通过对象访问
            System.out.println(Outter.this.msg);
            //Outter.this.msg:内部类中隐藏了一个外部类的对象
        }
    }
}

成员内部类不能定义静态变量,成员内部类必须要依赖于外部类,若成员内部类有静态属性,没有外部类对象也能访问了

JAVA类及类的成员_第4张图片

对于外部类来说,能否在外部类的静态方法中使用成员内部类? => 相当于在静态方法中调用成员变量 不行!比如:在外部类的main中创建内部类对象

 JAVA类及类的成员_第5张图片

成员内部类,一方面,作为外部类的成员:
             >调用外部类的结构
             >可以被static修饰
             >可以被4种不同的权限修饰
另一方面,作为一个类:
             >类内可以定义属性、方法、构造器等。
             >可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承
             >可以被abstract修饰

B.局部内部类:

JAVA类及类的成员_第6张图片

  小点:成员内部类和局部内部类,在编译之后,都会生成字节码文件。
     格式:成员内部类:外部类$内部类名:class
                局部内部类:外部类$数字 内部类名.class

    // 开发中很少见
	public void method() {
		// 局部内部类
		class AA {

		}
	}
在局部内部类的方法中(比如:show)如果调用局部内部类所声明的方法(比如:method)中的局部变量
  (比如:num)的话,要求此局部变量声明为final的。
  jdk 7及之前的版本,要求此局部变量显示的声明为final的
  jdk 8及之后的版本,可以省略final的声明
	public void method() {
	    //局部变量
		int num = 10;
		class AA {
			public void show() {
//				num = 20;//不可被修改
			}
		}
	}

C.静态内部类:

JAVA类及类的成员_第7张图片

这个自我感觉还是一个特殊的点:静态内部类可以有自己的成员变量。 

JAVA类及类的成员_第8张图片

D.匿名内部类:(Lambda表达式的前身)

使用匿名内部类有个前提条件:必须继承一个父类或实现一个接口,普通类和抽象类都可以,但最多只能继承一个父类,或实现一个接口

关于匿名内部类必须要知道的两点知识:

1、匿名内部类不能有构造器(构造方法),匿名内部类没有类名,不能定义构造器,构造方法的方法名是要与类名一致,但匿名内部类可以定义实例初始化块。

2、匿名内部类不可以是抽象类,匿名内部类不能有构造器,而抽象类可以有构造方法,java在创建匿名内部类的时候,会立即创建内部类的对象,而抽象类不能创建实例,

interface Father{
    public abstract void speak();
}
public class NIMingDemo {
    public static void main(String[] args) {
        Father f=new Father() {
            @Override
            public void speak() {
                System.out.println("粑粑:孩子,不你不想拉粑粑");
            }
            public void eatBaBa(){
                System.out.println("熊孩子:粑粑,我想拉粑粑");
            }
        };
        f.speak();
        f.eatBaBa(); //编译失败,不能调用eatBaBa()方法
    }
}

程序过程中, f.eatBaBa(); 会编译失败,提示不能调用eatBaBa()方法,因为 Father f=new Father()创建的是Father的对象,而非匿名内部类的对象。其实匿名内部类连名字都没有,咋实例对象去调用它的方法呢?但是f.speak()方法却可以执行,因为匿名内部类实现了接口Father的

speak()方法,因此可以借助Father的对象去调用。

如果想调用匿名内部类的自定义的eatBaBa()方法,有两个方法:

方法一、 事实上匿名内部类中隐含一个匿名对象,通过该方法可以直接调用eatBaBa()和speak()方法;具体代码如下:

interface Father{
    public abstract void speak();
}
public class NIMingDemo {
    public static void main(String[] args) {
        new Father() {
            @Override
            public void speak() {
                System.out.println("粑粑:孩子,不你不想拉粑粑");
            }
            public void eatBaBa(){
                System.out.println("熊孩子:粑粑,我想拉粑粑");
            }
        }.eatBaBa() ;
    }
}

只不过这个匿名对象只能使用一次!

方法二、 把eatBaBa()方法更改为speak()方法一样的使用,也就是说在Father接口中声明eatBaBa()方法,然后在匿名内部类中覆写此方法即可。这个就不贴代码了!

匿名内部类在多线程上的实现
匿名内部类最常用的情况就是在多线程的实现上,因为要实现多线程必须继承Thread类或是继承Runnable接口

public class ThreadDemo {
    public static void main(String[] args) {
        Thread t = new Thread() {
            public void run() {
                for (int i = 1; i <= 3; i++) {
                    System.out.print(i);
                }
            }
        };
        t.start();
    }
}
//运行结果:  123

关于内部类重点关注如下的3个问题
   4.1 如何实例化成员内部类的对象
   4.2 如何在成员内部类中区分调用外部类的结构

public class InnerClass_Test {
	public static void main(String[] args) {
		// 4.1实例化成员内部类的对象---------------------------------------
		// 创建Dog实例(静态的成员内部类):
		Person.Dog dog = new Person.Dog();
		dog.show();
		// 创建Bird实例(非静态的成员内部类):
//		Person.Bird brid = new Person.Bird();//错误的
		Person p = new Person();
		Person.Bird bird = p.new Bird();
		bird.sing();
		System.out.println();
		bird.display("黄鹂");
	}
}
class Person {
	String name = "小明";
	int age;
	public void eat() {
	}
	// 静态成员内部类
	static class Dog {
		String name;
		int age;
		public void show() {
		}
	}
	// 非静态成员内部类
	class Bird {
		String name = "杜鹃";
		public Bird() {
		}
		public void sing() {
			Person.this.eat();// 调用外部类的非静态属性
		}
//		4.2 在成员内部类中区分调用外部类的结构
		public void display(String name) {
			System.out.println(name);// 方法的形参// :黄鹂
			System.out.println(this.name);// 内部类的属性// :杜鹃
			System.out.println(Person.this.name);// 外部类的属性// :小明
		}
	}
}

 4.3 开发中局部类的使用

public class InnerClassTest1 {
	// 返回一个实现了Comparable接口的类的对象
    public Comparable getComparable() {
        // 创建一个实现了Comparable接口类:局部内部类
        // 方式一:
        class MyComparable implements Comparable{

            @Override
            public int compareTo(Object o) {
                return 0;
            }
        }
//        return new MyComparable();
        // 方式二:(匿名的方式:匿名内部类的匿名对象)
        return new Comparable() {
            @Override
            public int compareTo(Object o) {
                // TODO Auto-generated method stub
                return 0;
            }
        };
    }
	}

这篇博客非常好!!!!! 

JavaSE基础知识(二十)--Java内部类之为什么需要内部类_ruidianbaihuo的博客-CSDN博客

你可能感兴趣的:(java)