Chapter04面向对象【上】

第四章 面向对象

面向对象思想

面向对象思想的引入
前面我们讲过数组,当有多个数组都需要遍历时,我们可以将遍历的代码封装到方法中,需要遍历时,就调用相应的方法即可,提高代码的复用性。在对数组遍历的基础上继续增加需求,比如获取最值,数值逆序等,同样需要将这些功能封装到相应的方法中。这样继续封装会发现方法越来越多,于是就想能不能将这些方法继续进行封装呢?通过前面的讲解我们知道类是可以存放方法的,所以,我们就考虑使用类封装来这多个方法,将来再做数组的操作时,不用去找具体的方法,先找到这个类,然后使用这个类中的方法。这就是面向对象思想的编程方式。

1:面向对象思想
面向对象是基于面向过程的编程思想。

面向过程:强调的是每一个功能的步骤
面向对象:强调的是对象,然后由对象去调用功能

2:面向对象的思想特点

  • A:是一种更符合我们思想习惯的思想
  • B:可以将复杂的事情简单化
  • C:将我们从执行者变成了指挥者

3.把大象装进冰箱
面向过程:
A:打开冰箱门
B:装进大象
C:关闭冰箱门

代码体现;
class Demo {
    public static void main(String[] args) {
        open();
        in();
        close();
    }
                
    public static void open() {
        System.out.println("打开冰箱门");
    }
                
    public static void in() {
        System.out.println("装进大象");
    }
                
    public static void close() {
        System.out.println("关闭冰箱门");
    }
}

面向对象:
A:有哪些类呢?
B:每个类有哪些东西呢?
C:类与类直接的关系是什么呢?

把大象装进冰箱的分析? (如何分析有哪些类呢?UML。名词提取法。)

  • A:有哪些类呢?
    大象
    冰箱
    Demo
  • B:每个类有哪些东西呢?
    大象:
    进去
    冰箱:
    开门
    关门
    Demo:
    main方法
  • C:类与类直接的关系是什么呢?
    Demo中使用大象和冰箱类的功能。

代码体现:

class 大象 {
    public static void in() {
        System.out.println("装进大象");
    }
}
            
class 冰箱 {
    public static void open() {
        System.out.println("打开冰箱门");
    }
                
    public static void close() {
        System.out.println("关闭冰箱门");
    }
}
            
class Demo {
    public static void main(String[] args) {
        冰箱调用开门
        大象调用进去
        冰箱调用关门
    }
}

面向对象开发
就是不断的创建对象,使用对象,指挥对象做事情。

面向对象设计
其实就是在管理和维护对象之间的关系。

面向对象特征

封装(encapsulation)
继承(inheritance)
多态(polymorphism)

类与对象及其使用

区分:成员变量和局部变量的区别,静态变量和成员变量的区别

--确定成员变量和成员方法时要注意---
属性(成员变量)就是该事物(类)的描述信息
行为(成员方法)就是该事物(类)能够做什么

:是一组相关的属性和行为的集合
对象:是该类事物的具体体现

定义类其实就是定义类的成员(成员变量和成员方法)
1:成员变量 和以前定义变量是一样的,只不过位置发生了改变。在类中,方法外。
2:成员方法 和以前定义方法是一样的,只不过把static去掉,后面在详细讲解static的作用。

定义一个学生类

/*StudentDemo.java*/
class Student{
    String name;//默认null,堆中未初始化变量有默认值
    int age;//0
    String add;//null

    public void study(){
        System.out.println("学生学习");
    }

    public void eat(){
        System.out.println("学生吃饭");
    }

    public void sleep(){
        System.out.println("学生睡觉");
    }
}

class StudentDemo{
    public static void main(String[] args){
        Student s = new Student();
    
        System.out.println(s.name + "\t" + s.age + "\t" + s.add);
    
        s.name = "小白";//给成员变量赋值
        s.age = 10;
        s.add = "北京";
    
        System.out.println(s.name + "\t" + s.age + "\t" + s.add);
    
        s.study();//调用方法
        s.eat();
        s.sleep();
    
        System.out.println(p);//Student@19e0bfd   打印的是地址值!!!!
    }
}

创建对象:
类名 对象名 = new 类名();
对象名.成员变量
对象名.成员方法

/*PhoneDemo.java*/
class Phone{
    String brand;
    int price;
    String color;
    
    public void call(String name) {
        System.out.println("给"+name+"打电话");
    }
    
    public void sendMessage() {
        System.out.println("群发短信");
    }
    
    public void playGame() {
        System.out.println("玩游戏");
    }
}

class PhoneDemo{
    public static void main(String[] args){
        Phone p = new Phone();

        System.out.println(p.brand + "\t" + p.price + "\t" + p.color);

        p.brand = "魅族";
        p.price = 1000;
        p.color = "白色";
        System.out.println(p.brand + "\t" + p.price + "\t" + p.color);

        p.call("黄章");
        p.sendMessage();
        p.playGame();
    }
}

对象的内存图

一个对象的内存图,一个对象的基本初始化过程


一个对象的内存图
一个对象的内存图

两个对象的内存图,方法的共用


两个对象的内存图
两个对象的内存图

三个对象的内存图,其中有两个引用指向同一个(堆内的)对象
两个引用指向同一对象
两个引用指向同一对象

成员变量和局部变量的区别

(静态变量和成员变量的区别呢)

1.在类中的位置不同
成员变量 类中方法外
局部变量 方法内或者方法声明上

2.在内存中的位置不同
成员变量 堆内存
局部变量 栈内存

3.生命周期不同
成员变量 随着对象的存在而存在,随着对象的消失而消失
局部变量 随着方法的调用而存在,随着方法的调用完毕而消失

4.初始化值不同
成员变量 有默认的初始化值,可以不用赋值
局部变量 没有默认的初始化值,必须先定义,赋值,才能使用。
(栈内存的变量必须先赋值才能使用。
而堆内存中的变量有默认值,可以直接使用)

注:局部变量名称可以和成员变量同名,使用时采用就近原则。

class Variable{
    //成员变量
    int num = 10;//若未赋值,默认为0

    public void show(){
        int num2 = 20;//局部变量,必须赋值使用
    }
}

class VariableDemo{
    public static void main(String[] args){
        Variable v = new Variable();
    }
}

类类型形式参数问题

基本类型作为形式参数
引用类型(类类型)作为形式参数

//形式参数是基本类型
class Demo{
    public int sum(int a,int b){
        return a + b;
    }
}

//形式参数是引用类型
class Student{
    public void show(){
        System.out.println("我爱学习");
    }
}

class StudentDemo{
    //若一个方法的形参为类类型(引用类型)
    //调用时这里要传入这个类的一个对象。
    public void method(Student s){
        s.show();
    }
}

class ArgsTest{
    public static void main(String[] args){
        //形参是基本类型的调用
        Demo d = new Demo();
        int result = d.sum(10,20);
        System.out.println("result:"+result);

        //形参是引用类型的调用
        //需求:调用StudentDemo类中的method方法

        Student s = new Student();//s实际是一个地址
        StudentDemo sd = new StudentDemo();
        sd.method(s);//传入一个Student的实例(地址)。
    }
}

匿名对象

匿名对象:就是没有名字的对象。
是对象的一种简化表示形式

匿名对象的两种使用情况
1.对象调用方法仅一次的时候,多次时不适合。
匿名对象调用完毕成为垃圾,被垃圾回收器回收。
2.作为实际参数传递

class Student{
    public void show(){
        System.out.println("我爱学习");
    }
}

class StudentDemo{
    public void method(Student s){
        s.show();
    }
}

class NoNameDemo{
    public static void main(String[] args){
        Student s = new Student();
        s.show();
        s.show();//一个对象,调用两次
        //匿名对象
        //匿名对象调用方法
        new Student().show();
        new Student().show();//这里重新创建了一个新的对象

        //匿名对象作为实际参数传递
        StudentDemo sd = new StudentDemo();
        sd.method(new Student());

        new StudentDemo().method(new Student());
    }
}

封装(private)

/*StudentDemo.java*/

/*通过对象给成员变量赋值,可以赋一些非法数据,
这是不合理的。
应该在赋值之前先判断。判断在哪里做?
解决办法:
1,类提供修改成员变量值的方法并含验证
2,将类成员变量定为 private
(被private修饰的成员只能在本类中使用)
*/

class Student{
    String name;
    int age;//设为private,外界将不能访问。

    public void show(){
        System.out.println("姓名:"+name);
        System.out.println("年龄:"+age);
    }

    public void setAge(int a){//提供修改方法
        if(a>=0)
            age = a;
        else
            System.out.println("给定年龄错错误");
    }
}

class StudentDemo{
    public static void main(String[] args){
        Student s = new Student();
        s.show();
        System.out.println("-----------");

        s.name = "小白";
        s.age = 10;
        s.show();
        System.out.println("-----------");

        s.age = -27;//若age为private,则出错
        s.show();//-27赋值不合理
        System.out.println("-----------");

        s.setAge(-27);//可以进行判断
        s.show();//
        System.out.println("-----------");
    }
}

封装:指隐藏对象的属性和实现细节,仅对外提供公共访问方式。

好处:
隐藏实现细节,提供公共的访问方式
提高了代码的复用性
提高安全性。

封装原则:
将不需要对外提供的内容都隐藏起来。
把属性隐藏,提供公共方法对其访问。

private关键字:
是一个权限修饰符。
可以修饰成员(成员变量和成员方法)
被private修饰的成员只在本类中才能访问。

private最常见的应用:
把成员变量用private修饰
提供对应的getXxx()/setXxx()方法

一个标准的案例的使用

/*
private的应用:
把成员变量用private修饰
提供对用的getXxx()和setXxx()方法*/

class Student{
    private String name;
    private int age;

    public int getAge(){
        return age;
    }

    public String getName(){
        return name;
    }

    public void setAge(int a){
        age = a;
    }

    public void setName(String n){
        name = n;
    }
}

class StudentTest{
    public static void main(String[] args){
        Student s = new Student();

        //错误,访问不到
        //System.out.println(s.age);
        s.setAge(10);
        s.setName("小白");
        System.out.println(s.getName()+": "+s.getAge());
    }
}

this关键字

来看这样一段类定义

class Student{
    private String name;
    private int age;

    public int getAge(){
        return age;
    }

    public String getName(){
        return name;
    }

    public void setAge(int age){
        age = age;//根据就近原则,其实没有修改成员变量age
    }

    public void setName(String name){
        name = name;//根据就近原则,其实没有修改成员变量age
    }
}

这样在对象调用set方法时,其实没有改掉成员变量age和name。
由此引入 this

    public void setAge(int age){
        this.age = age;//可以改变成员变量age
    }

    public void setName(String name){
        this.name = name;//可以改变成员变量name
    }

this:代表所在类的对象引用
记住:
方法被哪个对象调用,this就代表那个对象

什么时候使用this呢?
1.局部变量隐藏成员变量(上例)
2.其他用法后面和super一起讲解

方法被哪个对象调用,this就代表那个对象
【0701this关键字的内存图解】


this关键字
this关键字
/*
private的应用:
把成员变量用private修饰
提供对用的getXxx()和setXxx()方法*/

class Phone{
    private String brand;
    private int price;
    private String color;

    public String getBrand(){
        return brand;
    }
    public int getPrice(){
        return price;
    }
    public String getColor(){
        return color;
    }
    public void setBrand(String brand){
        this.brand = brand;
    }
    public void setPrice(int price){
        this.price = price;
    }
    public void setColor(String color){
        this.color = color;
    }
}

class PhoneTest{
    public static void main(String[] args){
        Phone p = new Phone();
        System.out.println(p.getBrand()+"\t"+p.getPrice()+"\t"+p.getColor());

        p.setBrand("魅族");
        p.setPrice(999);
        p.setColor("黑白");
        System.out.println(p.getBrand()+"\t"+p.getPrice()+"\t"+p.getColor());    
    }
}

构造方法

构造方法作用:给对象的数据进行初始化

构造方法格式
方法名与类名相同
没有返回值类型,连void都没有
没有具体的返回值

构造方法注意事项---
如果你不提供构造方法,系统会给出默认构造方法
如果你提供了构造方法,系统将不再提供
注意:这时若还想用无参构造方法,要自己显式给出。
建议永远自己给出无参构造方法。

构造方法也是可以重载的

给成员变量赋值有两种方法:

  1. setXxx();
    2.带参构造方法
class Student{
    private String name;
    private int age;

    public Student(){
        System.out.println("这是无参构造方法");
    }

    public Student(String name){
        System.out.println("这是带String参构造方法");
        this.name = name;
    }

    public Student(int age){
        System.out.println("这是带int参构造方法");
        this.age = age;
    }

    public Student(String name,int age){
        System.out.println("这是带两参构造方法");
        this.age = age;
        this.name = name;
    }

    public void show(){
        System.out.println(name+": "+age);
    }
}

class ConstructDemo{
    public static void main(String[] args){
        //创建对象
        Student s1 = new Student();//调用无参构造方法
        System.out.println(s1);//打印地址
        s1.show();
        //创建对象
        Student s2 = new Student("小白");//调用带参构造方法
        System.out.println(s2);//打印地址
        s2.show();
        //创建对象
        Student s3 = new Student(10);//调用带参构造方法
        System.out.println(s3);//打印地址
        s3.show();
        //创建对象
        Student s4 = new Student("小黑",20);//调用带参构造方法
        System.out.println(s4);//打印地址
        s4.show();
    }
}

类的成员方法

成员方法其实就是我们前面讲过的方法

方法具体划分:
根据返回值

有明确返回值方法
返回void类型的方法

根据形式参数

无参方法
带参方法

一个基本类的标准写法


成员变量
构造方法
无参构造方法
带参构造方法
成员方法
getXxx()
setXxx()

给成员变量赋值的方式
无参构造方法+setXxx()
带参构造方法

一个基本类的标准代码案例

/*
一个标准代码的最终版(学生类)
*/

class Student{
    private String name;
    private int age;

    public Student(){
    }

    public Student(String name,int age){
        this.age = age;//构造方法给成员变量赋值
        this.name = name;
    }

    public void setName(String name){
        this.name = name;
    }

    public void setAge(int age){
        this.age = age;//set方法赋值
    }

    public String getName(){
        return name;
    }

    public int getAge(){
        return age;
    }

    public void show(){
        System.out.println(name+": "+age);
    }
}

class StudentDemo{
    public static void main(String[] args){
        Student s1 = new Student();//调用无参构造方法
        s1.setName("小白");
        s1.setAge(10);
        System.out.println(s1.getName()+": "+s1.getAge());
        s1.show();

        Student s2 = new Student("小黑",20);
        System.out.println(s2.getName()+": "+s2.getAge());
        s2.show();        
    }
}
/*
一个标准代码的最终版(手机类)
*/
class Phone{
    private String brand;
    private int price;
    private String color;

    public Phone(){
    }

    public void setBrand(String brand){
        this.brand = brand;
    }

    public void setPrice(int price){
        this.price = price;
    }

    public void setColor(String color){
        this.color = color;
    }

    public String getBrand(){
        return brand;
    }

    public int getPrice(){
        return price;
    }

    public String getColor(){
        return color;
    }
}

class PhoneDemo{
    public static void main(String[] args){
        Phone p = new Phone();//调用无参构造方法
        p.setBrand("魅族");
        p.setPrice(1000);
        p.setColor("黑白");
        System.out.println(p.getBrand()+"-"+p.getPrice()+"-"+p.getColor());
    }
}

类的初始化过程

Student s = new Student();在内存中做了哪些事情?

加载Student.class文件进内存
在栈内存为s开辟空间
在堆内存为学生对象开辟空间
对学生对象的成员变量进行默认初始化
对学生对象的成员变量进行显示初始化
通过构造方法对学生对象的成员变量赋值
学生对象初始化完毕,把对象地址赋值给s变量

创建对象做了哪些事情
创建对象做了哪些事情

static关键字

static修饰成员变量:针对多个对象共享成员变量的时候

/*
    定义一个人类
    
    姓名和年龄都是变化的,因为每个人的姓名和年龄是不同的。
    但是,我们现在选取的几个人都是中国人,他们的国籍是一样的。
    一样的国籍,我每次创建对象,在堆内存都要开辟这样的空间,
    我就觉得有点浪费了。怎么办呢? 
        针对多个对象有共同的成员变量的时候,
        Java就提高了一个关键字来修饰:static。
*/
class Person {
    //姓名
    String name;
    //年龄
    int age;
    //国籍
    //String country;
    static String country;//采用static,所有对象共享country
    
    public Person(){}
    
    public Person(String name,int age) {
        this.name = name;
        this.age = age;
    }
    
    public Person(String name,int age,String country) {
        this.name = name;
        this.age = age;
        this.country = country;
    }
    
    public void show() {
        System.out.println("姓名:"+name+",年龄:"+age+",国籍:"+country);
    }
}

class PersonDemo {
    public static void main(String[] args) {
        //创建对象1
        Person p1 = new Person("邓丽君",16,"中国");
        p1.show();
        
        //创建对象2
        //Person p2 = new Person("杨幂",22,"中国");
        //p2.show();
        Person p2 = new Person("杨幂",22);
        p2.show();//若country不是静态,则国籍为空
        
        //创建对象3
        //Person p3 = new Person("凤姐",20,"中国");
        //p3.show();
        Person p3 = new Person("凤姐",20);
        p3.show();//若country不是静态,则国籍为空
        
        p3.country = "美国";
        p3.show();//美国
        
        p1.show();//美国
        p2.show();//美国
    }
}

static 可以修饰成员变量和成员方法

static关键字特点

static 可以修饰成员变量和成员方法
1.随着类的加载而加载
2.优先于对象存在(对象在new以后才有)
3.被类的所有对象共享

这也是告诉我们是么时候使用静态关键字
若某个成员变量被所有对象共享,那么它应该定义为静态。

4.可以通过类名调用(也可以用对象调,建议用类名调用)
(main方法被虚拟机调用,直接 类名.main())

static的内存图解
static的内存图解

静态的内容存在于方法区的静态区
静态的东西随class的加载而加载

static关键字注意事项

A:在静态方法中是没有this关键字的

如何理解呢?
静态是随着类的加载而加载,this是随着对象的创建而存在。
静态比对象先存在。

B:静态方法只能访问静态的成员变量和静态的成员方法

静态方法:
成员变量:只能访问静态变量
成员方法:只能访问静态成员方法
非静态方法:
成员变量:可以是静态的,也可以是非静态的
成员方法:可是是静态的成员方法,也可以是非静态的成员方法。

简单记:静态只能访问静态。

class Teacher {
    public int num = 10;
    public static int num2 = 20;
    
    public void show() {
        System.out.println(num); //隐含的告诉你访问的是成员变量
        System.out.println(this.num); //明确的告诉你访问的是成员变量
        System.out.println(num2);//非静态方法可以访问静态变量
        
        function();//非静态访问非静态
        function2();//非静态访问静态
    }
    
    public static void method() {
        //无法从静态上下文中引用非静态 变量 num
        //System.out.println(num);
        System.out.println(num2);
        
        //无法从静态上下文中引用非静态 方法 function()
        //function();//静态不能访问非静态
        function2();//静态访问静态
    }
    
    public void function() {
    
    }
    
    public static void function2() {
    
    }
}

class TeacherDemo {
    public static void main(String[] args) {
        //创建对象
        Teacher t = new Teacher();
        t.show();
        System.out.println("------------");
        t.method();
    }
}

无法从静态上下文中引用非静态 变量 num
无法从静态上下文中引用非静态 方法 function()

静态变量和成员变量的区别

(成员变量和局部变量的区别呢?)

所属不同
静态变量属于类,所以也称为为类变量(随着类的加载而加载)
成员变量属于对象,所以也称为实例变量(对象变量)

内存中位置不同
静态变量存储于方法区的静态区
成员变量存储于堆内存

内存出现时间不同
静态变量随着类的加载而加载,随着类的消失而消失
成员变量随着对象的创建而存在,随着对象的消失而消失

调用不同
静态变量可以通过类名调用,也可以通过对象调用
成员变量只能通过对象名调用

main方法是静态的

main方法是静态的,因此main()里面调用的方法必须是静态的。
非静态的方法只能通过造一个对象来调用。

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

public:公共的,访问权限是最大的。由于main方法是被jvm调用,所以权限要够大。
static:静态的,不需要创建对象,通过类名就可以调用。方便jvm的调用。
void:方法的返回值是返回给调用者,而main方法是被jvm调用。返回内容给jvm没有意义。
main:是一个常见的方法入口。我见过的语言都是以main作为入口。
String[] args:这是一个字符串数组。值去哪里了?这个东西到底有什么用啊?怎么给值啊?
这个东西早期是为了接收键盘录入的数据参数的。
格式是:java MainDemo text1 text2 text3

/*MainDemo.java*/
class MainDemo {
    public static void main(String[] args) {
        //System.out.println(args); //[Ljava.lang.String;@175078b
        //System.out.println(args.length); //长度是0,没有元素
        //System.out.println(args[0]); //ArrayIndexOutOfBoundsException,没有元素,越界
        
        //编译完成后,使用 java MainDemo text1 text2 text3命令运行
        //接收数据后
        System.out.println(args); //打印地址
        System.out.println(args.length); //长度是3
        //System.out.println(args[0]); 
        for(int x=0; x<args.length; x++) {//输出后面的三个参数text1 text2 text3
            System.out.println(args[x]);
        }
    }
}

静态的一个应用(有小技巧)

在写好的工具类中使用静态

来写一个数组遍历

class ArrayDemo {
    public static void main(String[] args) {
        int[] arr = {28,55,37,46,19};
        //静态方法
        printArray(arr);
    }
    
    public static void printArray(int[] arr) {//静态方法
        for(int x=0; x<arr.length; x++) {
            if(x == arr.length-1)
                System.out.println(arr[x]);
            else 
                System.out.print(arr[x]+", ");
        }
    }
}

因为main方法是静态的,因此main()里面调用的方法必须是静态的。

万一遇到main调用非静态的的情况呢?

非静态的方法只能通过造一个对象来调用。

class ArrayDemo {
    public static void main(String[] args) {
        int[] arr = {28,55,37,46,19};
        //非静态方法
        ArrayDemo ad = new ArrayDemo();//可以这样造对象!
        ad.printArray(arr);//非静态的方法只能通过造一个对象来调用
    }
    //假设该方法不是静态的
    public void printArray(int[] arr) {
        for(int x=0; x<arr.length; x++) {
            if(x == arr.length-1) 
                System.out.println(arr[x]);
            else 
                System.out.print(arr[x]+", ");
        }
    }
}

我们现在的操作是跟数组相关的,所以,应该把这些操作定义到数组操作类中
定义一个数组的操作类,有了数组操作类之后再调用。
注1:在同一个文件夹下,类定义在两个文件中和定义在一个文件中其实一样的。
注2:编译的时候,只需编译主类即可,被调用的类已经由jvm自动编译。

三种实现方法
1.ArrayTool.java 中 public void printArray(int[] arr) {}
ArrayDemo.java 中
ArrayTool at = new ArrayTool();
at.printArray(arr);

2.ArrayTool.java 中 public static void printArray(int[] arr) {}
ArrayDemo.java 中
ArrayTool at = new ArrayTool();
at.printArray(arr);//对象可以访问静态、非静态方法。

3.ArrayTool.java 中 public static void printArray(int[] arr) {}
ArrayDemo.java 中
ArrayTool.printArray(arr);//方法改进为静态后,就可以直接通过类名调用

/*ArrayTool.java*/
class ArrayTool {
    //!!!!高能!!!!
    //把构造方法私有,外界就不能再创建对象了
    private ArrayTool(){}

    public static void printArray(int[] arr) {
        for(int x=0; x<arr.length; x++) {
            if(x == arr.length-1) {
                System.out.println(arr[x]);
            }else {
                System.out.print(arr[x]+", ");
            }
        }
    }
}

在一个类中,把构造方法设为私有,把成员方法设为静态。
这样,其他类只能通过类名来访问该类的方法。------高能!!

/*ArrayDemo.java*/
class ArrayDemo {
    public static void main(String[] args) {
        int[] arr = {28,55,37,46,19};
        //方法改进为静态后,就可以直接通过类名调用
        ArrayTool.printArray(arr);
    }
}

帮助文档的制作

项目中,我们一般拿到的不是 ArraryTool.java 源文件,
而是 ArrayTool.class 文件和它的帮助文档(使用说明)。

如何制作一个说明书呢?
A:写一个工具类
B:对这个类加入文档注释
怎么加呢?
加些什么东西呢?
C:用工具解析文档注释
javadoc工具
D:格式
javadoc -encoding UTF-8 -d 目录 -author -version ArrayTool.java
-encoding UTF-8是为了解决 编码GBK的不可映射字符 错误。
-d 的意思可以命令行中javadoc后看到。
目录:将生成的文档放在当前路径的这个目录里,若无,则自动创建。通常为 doc
.表示当前目录

制作帮助文档出错:
找不到可以文档化的公共或受保护的类:告诉我们类的权限不够

范例:

/**
* 这是针对数组进行操作的工具类
* @author Fitz
* @version V.10
*/
public class ArrayTool {
    
    //把构造方法私有,外界就不能在创建对象了
    /**
    * 这是私有构造
    */
    private ArrayTool(){}

    /**
    * 这是遍历数组的方法,遍历后的格式是:[元素1, 元素2, 元素3, ...]
    * @param arr 这是要被遍历的数组
    */
    public static void printArray(int[] arr) {
        System.out.print("[");
        for(int x=0; x<arr.length; x++) {
            if(x == arr.length-1) {
                System.out.println(arr[x]+"]");
            }else {
                System.out.print(arr[x]+", ");
            }
        }
    }
    
    /**
    * 这是获取数组中最大值的方法
    * @param  arr 这是要获取最大值的数组
    * @return 返回数组中的最大值
    */
    public static int getMax(int[] arr) {
        int max = arr[0];
        
        for(int x=1; x<arr.length; x++) {
            if(arr[x] > max) {
                max = arr[x];
            }
        }
        
        return max;
    }
    
    /**
    * 获取指定元素在数组中第一次出现的索引,如果元素不存在,就返回-1
    * @param arr 被查找的数组 
    * @param value 要查找的元素
    * @return 返回元素在数组中的索引,如果不存在,返回-1
    */
    public static int getIndex(int[] arr,int value) {
        int index = -1;
        
        for(int x=0; x<arr.length; x++) {
            if(arr[x] == value) {
                index = x;
                break;
            }
        }
        
        return index;
    }
}

命令行中执行
javadoc -d doc -author -version ArrayTool.java
若报 编码GBK的不可映射字符 错误,则在javadoc后添加参数 -encoding UTF-8
之后便在当前目录生成doc文件夹。

/*ArrayDemo.java*/
class ArrayDemo {
    public static void main(String[] args) {
        //定义数组
        int[] arr = {28,55,37,46,19};
        
        //遍历
        ArrayTool.printArray(arr);//通过类名调用静态方法

        //获取最值
        int max = ArrayTool.getMax(arr);
        System.out.println("max:"+max);
        
        //获取55的索引值
        int index = ArrayTool.getIndex(arr,55);
        System.out.println("index:"+index);
    }
}

将ArrayTool.class 和 ArrayDemo.class放于一个文件夹下即可。

JDK帮助文档

如何使用帮助文档

1:打开帮助文档
2:点击显示,找到索引,看到输入框
3:知道你要找谁?以Scanner举例
4:在输入框里面输入Scanner,然后回车
5:看包,Scanner属于java.util包
因此要导入:java.util.Scanner
(java.lang包下的类不需要导入,其他的全部需要导入。)

6:再简单的看看类的解释和说明,别忘了看看该类的JDK版本
7:看类的结构
成员变量 -> 字段摘要
构造方法 -> 构造方法摘要
成员方法 -> 方法摘要
8:学习构造方法
A:有构造方法 就创建对象
B:没有构造方法 成员可能都是静态的
9:看成员方法
A:左边
是否静态:如果静态,可以通过类名调用
返回值类型:人家返回什么,你就用什么接收。
B:右边
看方法名:方法名称不要写错
参数列表:人家要什么,你就给什么;人家要几个,你就给几个

以Math类为例:
由于Math类在java.lang包下,所以不需要导包。
特点:没有构造方法,因为它的成员全部是静态的。

例子:
1.Math类的一个方法:获取随机数random();

public static double random()
返回带正号的 double 值,该值大于等于 0.0 且小于
1.0。返回值是一个伪随机选择的数,在该范围内(近似)均匀分布。

class MathDemo {
    public static void main(String[] args) {
        //获取1-100之间的随机数
        for(int x=0; x<100; x++) {
            int number = (int)(Math.random()*100)+1;
            System.out.println(number);
        }
    }
}

2.猜数字小游戏(数据在1-100之间)
程序产生一个随机数(被猜的),玩家有多次机会猜
程序给出玩家猜大了还是猜小了的提示。直到玩家猜中。

import java.util.Scanner;

class MathDemo{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        System.out.println("Input a number(1-100):");

        int guessNum = sc.nextInt();
        int machineNum = (int)(Math.random()*100+1);

        while(true){
            if(guessNum!=machineNum){
                if(guessNum>machineNum)
                    System.out.print("Too big!");
                else
                    System.out.print("Too small!");

                System.out.println("Input a number again:");
                guessNum = sc.nextInt();
            }
            else{
                System.out.println("machine:"+guessNum+"\tplayer:"+machineNum+"\nYou win!");
                break;
            }
        }
    }
}

代码块(易错易混)

在Java中,使用{}括起来的代码被称为代码块。
根据其位置和声明的不同,可以分为局部代码块,构造代码块,静态代码块,同步代码块(多线程讲解)。

局部代码块
在方法中出现;限定变量生命周期,及早释放,提高内存利用率

构造代码块
在类中方法外出现;多个构造方法方法中相同的代码存放到一起做成构造代码块,每次调用构造都执行,
并且在构造方法前执行。无论书写的位置前后,都先执行构造代码块,最后执行构造方法。
作用:可以把--多个构造方法中的共同代码--放到一起,对对象进行初始化。

静态代码块 在类中方法外出现,加了static修饰
在类中方法外出现,并加上static修饰;
用于给类进行初始化,在加载的时候就执行,并且仅第一次使用该类时执行。
作用:一般是对类进行初始化。

面试题
静态代码块,构造代码块,构造方法的执行顺序?
静态代码块 -- 构造代码块 -- 构造方法
静态代码块:仅在第一次使用该类时,执行一次
构造代码块:每次调用构造方法都执行


class Code {
    //System.out.println("");//输出语句不能放在方法或代码块外执行
    static {
        int a = 1;
        System.out.println("静态代码块"+a);
    }
    //构造代码块
    {
        int x = 1;
        System.out.println("构造代码块"+x);
    }
    //构造方法
    public Code(){
        System.out.println("无参构造方法");
    }
    //构造方法
    public Code(int a){
        System.out.println("带参构造方法");
    }
    //构造代码块
    {
        int y = 2;
        System.out.println("构造代码块"+y);
    }
    //静态代码块
    static {
        int b = 2;
        System.out.println("静态代码块"+b);
    }
}

class CodeDemo {
    public static void main(String[] args) {
        //局部代码块
        {
            int x = 1;
            System.out.println("局部代码块"+x);
        }
        //找不到符号,代码块外x不再起作用
        //System.out.println(x);
        {
            int y = 2;
            System.out.println("局部代码块"+y);
        }
        System.out.println("---------------");
        
        Code c = new Code();    
        System.out.println("---------------");
        Code c2 = new Code();
        System.out.println("---------------");
        Code c3 = new Code(1);
    }
}

执行结果:

局部代码块1
局部代码块2
-------------
静态代码块1
静态代码块2
构造代码块1
构造代码块2
无参构造方法
-------------
构造代码块1
构造代码块2
无参构造方法
-------------
构造代码块1
构造代码块2
带参构造方法

-- 在同一个类中,静态代码块优先于main() 方法执行 --

/*
    写程序的执行结果。
    
    测试开始
    我是main方法
    Student 静态代码块
    Student 构造代码块
    Student 构造方法
    Student 构造代码块
    Student 构造方法
*/
class Student {
    static {
        System.out.println("Student 静态代码块");
    }
    
    {
        System.out.println("Student 构造代码块");
    }
    
    public Student() {
        System.out.println("Student 构造方法");
    }
}

class CodeDemo {
    static {
        System.out.println("测试开始");//最先执行!!
    }
    
    public static void main(String[] args) {
        System.out.println("我是main方法");
        
        Student s1 = new Student();//用到Student()才加载Student.class
        System.out.println("----------");
        Student s2 = new Student();
    }
}

--先加载 class CodeDemo{} ,再加载 static 静态代码块,最后才调用 main()方法--

继承

继承概述

多个类中存在相同属性和行为时,- 将这些内容抽取到单独一个类中, -
那么多个类无需再定义这些属性和行为,只要继承那个类即可。

通过extends关键字可以实现类与类的继承
class 子类名 extends 父类名 {}

单独的这个类称为父类,基类或者超类;这多个类可以称为子类或者派生类。
有了继承以后,我们定义一个类的时候,可以在一个已经存在的类的基础上,还可以定义自己的新成员。

好处:
A:提高了代码的复用性
多个类相同的成员可以放到同一个类中
B:提高了代码的维护性
如果功能的代码需要修改,修改一处即可
C:让类与类之间产生了关系,是多态的前提
其实这也是继承的- 一个弊端 -:类的耦合性很强

开发的原则:低耦合,高内聚。
耦合:类与类的关系
内聚:就是自己完成某件事情的能力

class Person {
    public void eat() {
        System.out.println("吃饭");
    }    
    public void sleep() {
        System.out.println("睡觉");
    }
}

class Student extends Person {}
class Teacher extends Person {}

class ExtendsDemo {
    public static void main(String[] args) {
        Student s = new Student();
        s.eat();
        s.sleep();
        System.out.println("-------------");        
        Teacher t = new Teacher();
        t.eat();
        t.sleep();
    }
}

Java中继承的特点

Java只支持单继承,不支持多继承。
一个类只能有一个父类,不可以有多个父类。
class SubDemo extends Demo{} //ok
class SubDemo extends Demo1,Demo2...//error
Java支持多层继承(继承体系)
class A{}
class B extends A{}
class C extends B{}

Java中继承的注意事项

1.子类只能继承父类所有非私有的成员(成员方法和成员变量)
其实这也体现了继承的- 另一个弊端 -:打破了封装性
2.子类不能继承父类的构造方法,但是可以通过super(后面讲)关键字去访问父类构造方法。
3.不要为了部分功能而去继承
class A {
public void show1(){}
public void show2(){}
}
class B {
public void show2(){}
public void show3(){}
}
//我们发现B类中出现了和A类一样的show2()方法,所以,我们就用继承来体现
class B extends A {
public void show3(){}
}

这样其实不好,因为这样你不但有了show2(),还多了show1()。
有可能show1()不是你想要的。

我们到底在什么时候使用继承呢?
继承中类之间体现的是:”is a”的关系。

继承中成员变量的关系

在子类方法中访问一个变量
首先在子类局部范围找
然后在子类成员范围找
最后在父类成员范围找(肯定不能访问到父类局部范围)
如果还是没有就报错。(不考虑父亲的父亲…)

class Father {
    public int num = 10;//父类成员变量
    
    public void method() {
        int num = 50;//父类局部变量
    }
}

class Son extends Father {
    public int num2 = 20;//子类成员变量
    public int num = 30;
    
    public void show() {
        int num = 40;//子类局部变量
        System.out.println(num);
        System.out.println(num2);
    }
}

class ExtendsDemo {
    public static void main(String[] args) {
        Son s = new Son();
        s.show();
    }
}

super关键字

class Father {
    public int num = 10;
}

class Son extends Father {
    public int num = 20;
    
    public void show() {
        int num = 30;
        System.out.println(num);//30
        System.out.println(this.num);//20
        System.out.println(super.num);//10
    }
}

class ExtendsDemo5 {
    public static void main(String[] args) {
        Son s = new Son();
        s.show();
    }
}

super的用法和this很像
this代表本类对应的引用。
super代表父类存储空间的标识(可以理解为父类引用)
用法(this和super均可如下使用)
访问成员变量
this.成员变量 super.成员变量
访问构造方法(子父类的构造方法问题讲)
this(para) super(para)
访问成员方法(子父类的成员方法问题讲)
this.成员方法() super.成员方法()

继承中构造方法的关系

子类中所有的构造方法默认都会先访问父类中的无参构造方法
为什么呢?
因为子类会继承父类中的数据,可能还会使用父类的数据。
所以,子类初始化之前,一定要先完成父类数据的初始化。
- 每一个构造方法的第一条语句默认都是:super() -

class Father {
    int age;

    public Father() {
        System.out.println("Father的无参构造方法");
    }
    public Father(String name) {
        System.out.println("Father的带参构造方法");
    }
}

class Son extends Father {
    public Son() {
        //super();//自动加上,要有这个观念!!!!
        System.out.println("Son的无参构造方法");
    }
    public Son(String name) {
        //super();//自动加上
        System.out.println("Son的带参构造方法");
    }
}    

class ExtendsDemo {
    public static void main(String[] args) {
        Son s = new Son();
        System.out.println("------------");
        Son s2 = new Son("小白");
    }
}

"Father的无参构造方法
Son的无参构造方法
------------
Father的无参构造方法
Son的带参构造方法"

注:如果你提供了构造方法,系统将不再提供。
因此,当我们仅显式给出父类带参构造方法,系统将不会再给出无参构造。

- 如果父类中没有无参构造方法,该怎么办呢? -
 1.子类通过super去显式调用父类其他的带参的构造方法
   2.子类通过this去调用本类的其他构造方法
     而这里的其它构造方法也必须首先访问了父类构造

一定要注意:
super(…)或者this(….)必须出现在第一条语句山
否则,就会有父类数据的多次初始化(因为这时系统会自动加上super()或者this())

综上:

子类中所有的构造方法都必须能访问父类中构造方法(默认找无参,若无,则手动找带参)。
若子类构造没有指明super\this,则系统默认加上 super();

class Father {
    public Father(String name) {//本类没有无参构造方法
        System.out.println("Father的带参构造方法");
    }
}

class Son extends Father {
    public Son() {
        super("");//父类没有无参构造,手动调用父类带参构造
        System.out.println("Son的无参构造方法");
        //super("");//若放在后面,开始系统自动调用super(),报错
        //因为父类没有无参构造
    }
    
    public Son(String name) {
        //super("");//手动调用父类带参构造,或以下
        this();//手动调用本类无参构造,进而调用父类构造
        System.out.println("Son的带参构造方法");
    }
}

class ExtendsDemo7 {
    public static void main(String[] args) {
        Son s = new Son();
        System.out.println("----------------");
        Son ss = new Son("林青霞");
    }
}

Father的带参构造方法
Son的无参构造方法
------------
Father的带参构造方法
Son的无参构造方法
Son的带参构造方法

总结:写好子类后,要检查是否能调用父类的某一构造方法。

看程序写结果
面试题1.

class Fu{
    public int num = 10;
    public Fu(){
        System.out.println("fu");
    }
}
class Zi extends Fu{
    public int num = 20;
    public Zi(){
        System.out.println("zi");
    }
    public void show(){
        int num = 30;
        System.out.println(num); //30
        System.out.println(this.num); //20
        System.out.println(super.num); //10
    }
}
class ExtendsTest {
    public static void main(String[] args) {
        Zi z = new Zi();
        z.show();
    }
}

fu
zi
30
20
10

A:成员变量 就近原则
  B:this和super的问题
    this访问本类的成员
    super访问父类的成员
  C:子类构造方法执行前默认先执行父类的无参构造方法
  D:一个类的初始化过程
    成员变量进行初始化
     默认初始化
     显式初始化
    构造方法初始化

面试题2.易错!!!!

class Fu {
    static {
        System.out.println("静态代码块Fu");
    }
    {
        System.out.println("构造代码块Fu");
    }
    public Fu() {
        System.out.println("构造方法Fu");
    }
}

class Zi extends Fu {
    static {
        System.out.println("静态代码块Zi");
    }
    {
        System.out.println("构造代码块Zi");
    }
    public Zi() {
        System.out.println("构造方法Zi");
    }
}

class ExtendsTest {
    public static void main(String[] args) {
        Zi z = new Zi();
    }
}

静态代码块Fu
静态代码块Zi
构造代码块Fu
构造方法Fu
构造代码块Zi
构造方法Zi

- 说明 -
类加载器准备加载Zi的时候,发现Zi extends Fu,因此先加载Fu。
静态的内容随着类的加载而加载。因此先执行Fu的静态代码块,再执行Zi的静态代码块。
类加载完成后进行构造。要走子构造,要先走父构造;走父构造,要先走父构造代码块。
因此:
父静态代码块-子静态代码块-父构造代码块-父构造方法-子构造代码块-子构造方法

面试题3.没看懂!!!

/*
    看程序写结果:
        A:成员变量的问题
            int x = 10; //成员变量是基本类型
            Student s = new Student(); //成员变量是引用类型
        B:一个类的初始化过程
            成员变量的初始化
                默认初始化
                显示初始化
                构造方法初始化
        C:子父类的初始化(分层初始化)
            先进行父类初始化,然后进行子类初始化。
            
    结果:
        YXYZ
        
    问题:
        虽然子类中构造方法默认有一个super()
        初始化的时候,不是按照那个顺序进行的。
        而是按照分层初始化进行的。
        它仅仅表示要先初始化父类数据,再初始化子类数据。
*/
class X {
    Y b = new Y();
    X() {
        System.out.print("X");
    }
}

class Y {
    Y() {
        System.out.print("Y");
    }
}

public class Z extends X {
    Y y = new Y();
    Z() {
        //super();有没有都一样。
        System.out.print("Z");
    }
    public static void main(String[] args) {
        new Z(); 
    }
}

继承中成员方法的关系

继承中成员方法的关系:
  A:子类中的方法和父类中的方法声明不一样,这个太简单。
  B:子类中的方法和父类中的方法声明一样,这个该怎么玩呢?
   通过子类对象调用方法:
     a:先找子类中,看有没有这个方法,有就使用,结束
     b:若没有,再看父类中,有没有这个方法,有就使用,结束
     c:如果没有就报错。

class Father {
    public void show() {
        System.out.println("show Father");
    }
}

class Son extends Father {
    public void method() {
        System.out.println("method Son");
    }
    
    public void show() {//方法重写
        System.out.println("show Son");
    }
}

class ExtendsTest {
    public static void main(String[] args) {
        //创建对象
        Son s = new Son();
        s.show();//show son
        s.method();//method son
        //s.fucntion(); //找不到符号
    }
}

继承的内存图?

方法重写概述

方法重写:子类中出现了和父类中方法声明(返回值类型参数列表)一模一样的方法。
也被称为方法覆盖,方法复写。

方法重载:
  本类中出现的方法名一样,参数列表不同的方法。与返回值(可以不同)无关。

子类对象调用方法的时候:
  先找子类本身,若无,再找父类。

方法重写的应用:
  当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法。
  这样,即沿袭了父类的功能,又定义了子类特有的内容。

子类重写了父类某方法后,若子类还想使用该方法,怎么办?super

class Phone {
    public void call(String name) {
        System.out.println("给"+name+"打电话");
    }
}

class NewPhone extends Phone {
    public void call(String name) {//重写了父类call方法
        //System.out.println("给"+name+"打电话");
        super.call(name);//还想使用父类功能,使用super
        System.out.println("可以听天气预报了");
    }
}

class ExtendsDemo {
    public static void main(String[] args) {
        NewPhone np = new NewPhone();
        np.call("小白");
    }
}

方法重写的注意事项

方法重写的注意事项
  A:父类中私有方法不能被重写
    因为父类私有方法子类根本就无法继承
  B:子类重写父类方法时,访问权限不能更低
    最好就一致
  C:父类静态方法,子类也必须通过静态方法进行重写
    其实这个算不上方法重写,但是现象确实如此,至于为什么算不上方法重写,多态中我会讲解
子类重写父类方法的时候,最好声明一模一样。

两个面试题

1:方法重写和方法重载的区别?方法重载能改变返回值类型吗?

方法重写:
在子类中,出现和父类中一模一样的方法声明的现象。
方法重载:
同一个类中,出现的方法名相同,参数列表不同的现象。

方法重载能改变返回值类型,因为它和返回值类型无关。

Override:方法重写
Overload:方法重载

2:this关键字和super关键字分别代表什么?以及他们各自的使用场景和作用。

this:代表当前类的对象引用
super:代表父类存储空间的标识。(可以理解为父类的引用,通过这个东西可以访问父类的成员)

场景:
成员变量:
this.成员变量
super.成员变量
构造方法:
this(...)
super(...)
成员方法:
this.成员方法
super.成员方法

继承案例

1.学生类继承人类

class Person{
    private String name;
    private int age;

    public Person(){//不能去掉,继承它的子类要调用
    //因为下面写了带参构造,系统不会给出无参构造,需显式给出
    }

    public Person(String name,int age){
        this.name = name;
        this.age = age;
    }

    public String getName(){
        return name;
    }

    public int getAge(){
        return age;
    }

    public void setName(String name){
        this.name = name;
    }

    public void setAge(int age){
        this.age = age;
    }
    private void say(){
        System.out.println("人说话");
    }
}

class Student extends Person{
    public Student(){

    }

    public Student(String name,int age){
        super(name,age);
    }
}

class ExtendsTest{
    public static void main(String[] args){
        Student s1 = new Student();
        s1.setAge(10);
        s1.setName("小白");
        System.out.println("我是Student"+s1.getName()+",年龄"+s1.getAge());

        Student s2 = new Student("小黑",20);
        System.out.println("我是Student"+s2.getName()+",年龄"+s2.getAge());
    }
}
/*    猫:
        成员变量:姓名,年龄,颜色
        构造方法:无参,带参
        成员方法:
            getXxx()/setXxx()
            eat()
            palyGame()
    狗:
        成员变量:姓名,年龄,颜色
        构造方法:无参,带参
        成员方法:
            getXxx()/setXxx()
            eat()
            lookDoor()            
    共性:
        成员变量:姓名,年龄,颜色
        构造方法:无参,带参
        成员方法:
            getXxx()/setXxx()
            eat()

    把共性定义到一个类中,这个类的名字叫:动物。
    动物类:
        成员变量:姓名,年龄,颜色
        构造方法:无参,带参
        成员方法:
            getXxx()/setXxx()
            eat()            
    猫:    
        构造方法:无参,带参
        成员方法:palyGame()
    狗:
        构造方法:无参,带参
        成员方法:lookDoor()
*/
class Animal{
    private String name;
    private int age;
    private String color;

    public Animal(){

    }
    public Animal(String name,int age,String color){
        this.name = name;
        this.age = age;
        this.color = color;
    }

    public void setName(String name){
        this.name = name;
    }
    public void setAge(int age){
        this.age = age;
    }
    public void setColor(String color){
        this.color = color;
    }

    public String getName(){
        return name;
    }
    public int getAge(){
        return age;
    }
    public String getColor(){
        return color;
    }

    public void eat(){
        System.out.println("eat");
    }
}

class Dog extends Animal{
    public Dog(){

    }
    public Dog(String name,int age,String color){
        super(name,age,color);
    }

    public void watchDoor(){
        System.out.println("The dog watch the door.");
    }
}

class Cat extends Animal{
    public Cat(){

    }
    public Cat(String name,int age,String color){
        super(name,age,color);
    }

    public void playGame(){
        System.out.println("The cat plays game.");
    }
}

class ExtendsTest{
    public static void main(String[] args){
        Dog d1 = new Dog();
        d1.setName("小白");
        d1.setAge(10);
        d1.setColor("白");
        System.out.println("小狗"+d1.getName()+d1.getAge()+"岁 "+d1.getColor()+"色。");
        d1.watchDoor();

        Dog d2 = new Dog("小黑",20,"黑");
        System.out.println("小狗"+d2.getName()+d2.getAge()+"岁 "+d2.getColor()+"色。");
        d2.watchDoor();

        Cat c = new Cat("小花",5,"花");
        System.out.println("小猫"+c.getName()+c.getAge()+"岁 "+c.getColor()+"色。");
        c.playGame();
    }
}

final关键字

final可以修饰类,方法,变量

特点:
  final可以修饰类,该类不能被继承,即不能用作父类。
  final可以修饰方法,该方法不能被重写(覆盖,复写)。
  final可以修饰变量,该变量不能被重新赋值。因为这个变量其实就是常量。

常量分为:
  A:字面值常量
    "hello",10,true
  B:自定义常量
    final int x = 10;

面试题(final的注意事项)

1.final修饰局部变量的问题
  修饰基本类型:基本类型的值不能发生改变。
  修饰引用类型:引用类型的(栈内存)地址值不能发生改变,
         但是,该对象的堆内存的值(成员变量等)是可以改变的。

class Student {
    int age = 10;
}

class FinalDemo {
    public static void main(String[] args) {
        //局部变量是基本数据类型
        int x = 10;
        x = 100;
        System.out.println(x);
        final int y = 10;
        //无法为最终变量y分配值
        //y = 100;
        System.out.println(y);
        System.out.println("--------------");
        
        //局部变量是引用数据类型
        Student s = new Student();
        System.out.println(s.age);
        s.age = 100;
        System.out.println(s.age);
        System.out.println("--------------");
        
        final Student ss = new Student();
        System.out.println(ss.age);
        ss.age = 100;
        System.out.println(ss.age);
        
        //重新分配内存空间
        //无法为最终变量ss分配值
        ss = new Student();
        //无法为最终变量ss分配值
        ss = s;
    }
}

2.final修饰变量的初始化时机
  A:被final修饰的变量只能赋值一次。
  B:初始化时机在构造方法完毕前。(非静态的常量)

class Demo {
    //int num = 10;
    //final int num2 = 20;
    
    int num;
    final int num2;
    
    {
        //num2 = 10;//代码块优先于构造方法
    }
    
    public Demo() {
        num = 100;
        //无法为最终变量num2分配值
        num2 = 200;
    }
}

class FinalDemo {
    public static void main(String[] args) {
        Demo d = new Demo();
        System.out.println(d.num);
        System.out.println(d.num2);
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容