【Java基础】类和对象

面向对象

对象

  • 属性
  • 行为

好处

  • 模块性

  • 信息隐藏

  • 代码复用

继承

Inheritance

关键字:extents

接口

interface

关键字:implements

实现该接口的类必须重写接口的所有方法

package

Java包(package)详解 (biancheng.net)

相当于一个文件夹,里面有很多文件或子文件夹

导入关键字 import 建议用那个类就导入那个类,也可以导入*

package语句要放在第一行,且只能有一个,前面可以有注释

import语句在package和类之间,可以有多个,且没有顺序要求

命名:全部小写

公司名.项目名.模块名

类和对象

在这里插入图片描述

访问修饰符

access modifiers

访问权限修饰符同类同包子类不同包
公开的publicyesyesyesyes
受保护的protectedyesyesyesno
默认的不写(default)yesyesnono
私有的privateyesnonono

用于修饰类的 方法属性,以及的访问权限

  • 外部类只能是public 或default
  • default是默认的,不能写出来,否则会报错

命名

  • 类名的第一个字母应该是大写的
  • 方法的第一个单词应该是动词,小驼峰

属性

初始化

可以在生命属性的时候赋予初始值,但是在逻辑处理方面具有局限性,可以在构造函数中赋值

方法

重载

方法名相同,参数列表不同

不能仅仅依据修饰符或者返回类型的不同来重载方法。

重载方法应该少用,因为它们会使代码的可读性大大降低

构造函数

constructors

  • 构造函数没有返回值,有访问修饰符

  • 构造函数可以有多个

  • 如果没有编写构造函数,编译器会自动创建一个无参构造函数

    这个缺省的无参构造函数会调用父类的无参构造函数

    如果没有显式指定父类,则都有一个隐式的父类Object类

参数

可变参数

可变长参数
如果不确定有多少个参数

可以在最后一个参数的类型后面添加一个省略号(三个点,…) ,然后添加一个空格和参数名。然后可以使用该参数的任意数量(包括无)调用该方法。

可变参数可以保证无法传入null,因为传入0个参数时,接收到的实际值是一个空数组而不是null。

public class Test {
    public static void main(String[] args) {
        int[] a={1,2,3,4,5};
        testVarargs(1,2,3);//123
        testVarargs(a);//12345

    }
    public static void testVarargs(int... varargs){
        for (int i = 0; i < varargs.length; i++) {
            System.out.print(varargs[i]);
        }
    }
}

不用提前创建数组了

如果与重载方法冲突,会优先匹配参数固定的方法

值传递和引用传递

  • 值调用(call by value): 在参数传递过程中,形参和实参占用了两个完全不同的内存空间。形参所存储的内容是实参存储内容的一份拷贝。实际上,Java对象的传递就符合这个定义,只不过形参和实参所储存的内容并不是常规意义上的变量值,而是变量的地址。

Java 只有值传递

  • 对于基本数据类型,就是值的拷贝,在方法内修改,并不影响方法外面
  • 对于引用类型的参数,也是值的拷贝,不过这个值是对象的地址,在方法内修改,会影响到方法外面
  • 引用调用(call by reference) : 在参数传递的过程中,形参和实参完全是同一块内存空间,两者不分彼此。 实际上,形参名和实参名只是编程中的不同符号,在程序运行过程中,内存中存储的空间才是最重要的。不同的变量名并不能说明占用的内存存储空间不同。

实例化

Point originOne = new Point(23, 94);

必须使用new来创建对象

new运算符通过为新对象分配内存并返回对该内存的引用来实例化类。new运算符还调用对象构造函数。

新运算符返回的引用不必分配给变量。它也可以直接用在表达式中。例如:

int height = new Rectangle().height;

请注意,在执行这个语句之后,程序不再有对创建的 Rectangle 的引用,因为程序从来不在任何地方存储引用。该对象未被引用,其资源可以由 Java 虚拟机自由回收。

使用对象

在类内可以直接使用属性

在类外需要跟上一个点

垃圾收集器

一些面向对象的语言要求您跟踪您创建的所有对象,并在不再需要它们时显式地销毁它们。显式地管理内存是冗长而且容易出错的。

Java 平台允许您创建任意多的对象(当然,受到系统可以处理的内容的限制) ,而且您不必担心破坏它们。当确定对象不再被使用时,JRE 就会删除它们。这个过程称为垃圾收集。

如果没有对该对象的更多引用,则该对象有资格进行垃圾收集。当变量超出作用域时,保存在变量中的引用通常会被删除。或者,可以通过将变量设置为特殊值 null 来显式删除对象引用。请记住,一个程序可以对同一个对象有多个引用; 在该对象符合垃圾收集条件之前,必须删除对该对象的所有引用。

this

在方法内部,可以使用一个隐含的变量this,它始终指向当前实例。

因此,通过this.field就可以访问当前实例的字段。

如果没有命名冲突,可以省略this。

class Person{
    public String name;
    public int age;
    Person(String name,int age){
        this.name=name;//这里的this指向当前的对象
        this.age=age;
    }
    Person(){
        //在一个构造函数中调用另一个构造函数,形式:this(参数列表),而且必须在第一行
        this("张三",18);
    }
    public void run(){
        System.out.println("跑");
    }
    public void eat(){
        this.run();//可以通过this调用类的其他方法
        System.out.println("吃");
    }
}

Static

  • 关键字:static

  • 静态变量又称类变量;静态方法又称类方法

  • 静态变量不属于具体的某个对象,是所有的对象共有

  • 内存:类的静态变量,jdk8(包含jdk8)之后,存在堆中;之前存在方法区的静态域中

  • 产生:类变量和类方法在类加载的时候就已经生成了

  • 访问:类名.静态变量名(推荐);对象名.静态变量名

​ 类名.静态方法名(推荐);对象名.静态方法名

  • 静态方法只能访问静态变量,类方法中无this的参数
  • 非静态方法可以访问所有的成员
  • 静态方法的应用场景:当方法中不涉及任何和对象相关的成员,可以设计成静态方法来提高开发效率。比如工具类中的utils

常量

1.常量的值不可以改变

2.定义变量的时候,如果加上final修饰符,这个变量就变成了常量:

经常和static搭配 static final double PI = 3.141592653589793;

3.根据习惯,常量名通常全部大写,多个单词之间用下滑线分割

可以修饰类,属性,方法,局部变量

  • 修饰类,类不能被继承

    该类不能继承,但是可以实例化对象

    也没必再将方法修饰成final

  • 修饰父类的方法,则子类不能重写父类的该方法

    子类不能重写,但是可以使用

  • 修饰属性,则不能被修改,又称常量

    一般用大写字母 和 _ 来命名:;例如 TOTAL_NUMBER

    必须在定义时,或者构造器中,或者代码块中,赋初值

    如果final修饰的属性是静态的,则只能在定义时,或着在静态代码块中赋初值,不能在构造器中

  • 修饰局部变量,则不能被修改,称为局部常量

注意事项

  • final 不能修饰构造器

  • final和static搭配使用效率更高,编译器底层做了优化

    两者顺序可以颠倒

  • 包装类(Integetr,Doube ,Float ,Blooean)都是final类型,String也是final类型

    不是基本数据类型

代码块

代码块的基本介绍
  • 代码化块,又称初始化块

  • 没有名字,参数,返回值

  • 基本格式 [static] { 代码语句};

  • 最后的分号可写可不写

代码块的分类
  • 代码块只有一个可以选择的修饰符:static
  • 带有static的是静态代码块;不带的是普通代码块
代码块的执行
  • 代码块不通过类或对象显式调用,而是隐式调用
  • 静态代码块实在类被加载的时候执行,且只被执行一次
  • 普通代码块是在每创建一次对象的时候,就被执行一次
静态代码块,普通代码块,构造器的执行顺序
  • 创建对象需要先加载类的信息,因此静态代码块会先执行,同时静态属性初始化

静态属性和静态代码块属于同一等级,同时存在时,执行顺序根据定义时的顺序来

  • 然后是执行构造器,

​ 但是构造器的第一行是调用父类的构造器,

​ 然后默认隐含执行父类普通代码块,和普通属性的初始化

普通属性和普通代码块属于同一等级,同时存在时,执行顺序根据定义时的顺序来最后才是构造器的代码

类的加载
  • 当创建该类的对象的时候,类被加载
  • 子类创建对象时候,父类会被先加载,子类后被加载
  • 当使用静态的成员或方法时,类会被加载
代码块的用处
  • 相当于是对构造器的补充
  • 提高代码的复用
  • Java 编译器将初始化器块复制到每个构造函数中。因此,可以使用此方法在多个构造函数之间共享一个代码块。
当存在继承关系的时候
  • 程序的执行是先加载类的信息:

​ 先加载父类的信息,因此先执行父类的静态代码块和静态属性的初始化

​ 在加载子类的信息,因此再执行子类的静态代码块和静态属性的初始化

  • 然后执行子类的构造器

​ 但是第一行是调用父类的构造器,

​ 因此接着执行父类的执行父类的普通代码块和普通属性的初始化,再执行父类的构造器

​ 最后执行子类的普通代码块和普通属性的初始化,子类的构造器

注意事项
  • 静态代码块只能调用静态属性和静态方法
  • 普通代码块可以调用任意属性和任意方法
静态初始化块
static{
    
}

一个类可以有任意数量的静态初始化块,它们可以出现在类体的任何位置。

运行时系统保证按照静态初始化块在源代码中出现的顺序调用它们。

嵌套类

在这里插入图片描述

嵌套类分为两类: 非静态类和静态类

非静态的嵌套类又被称为内部类

内部类又分为

  • 在一个类(外部类)中直接定义的内部类;
  • 在一个方法(外部类的方法)中定义的内部类;
  • 匿名内部类。

深入理解java嵌套类、内部类

静态嵌套类

  • 用static修饰
  • 可以访问外部类的所有静态成员,不可以访问非静态成员
  • 当外部类和把内部类重名时,默认遵守就近原则;调用外部类属性的方法:外部类名.外部类属性名//不用写this,是因为static可以访问的都是静态属性,可以通过类名直接访问
		
		public class Test {
		    public static void main(String[] args) {
		        //外部其他类访问静态内部类
		        //通过类名直接创建,前提是可以访问
		        Outer.Inner inner = new Outer.Inner();
		        //在外部类中编写一个方法,返回静态内部类实例
		        Outer.Inner inner2 = Outer.getInner();
		        
		    }
		}
		class Outer{
		    
		    static class Inner{
		        public void funcInner(){
		            System.out.println("成员内部类的方法");
		        }
		    }
		    public static  Inner getInner(){//将其声明为静态,可以通过类名直接访问
		        Inner inner = new Inner();
		        return inner;
		    }
		}

内部类

不能定义或声明静态成员

成员内部类
  • 可以访问外部类的所有成员

  • 可以添加任意访问修饰符

  • 作用域在类内,在成员方法中创建实例

  • 当外部类和把内部类重名时,默认遵守就近原则;调用外部类属性的方法:外部类名.this.外部类属性名//谁调用该方法或代码块,则this指向谁

  • 访问方式

    		
    		public class Test {
    		    public static void main(String[] args) {
    		        //通过外部类的方法,来创建调用成员内部类
    		       Outer outer1 = new Outer();
    		        outer1.func();
    		        //直接创建成员内部类,通过外部类的对象
    		        Outer.Inner   inner = outer1.new   Inner();
    		        //通过外部类的方法返回一个内部类
    		        Outer.Inner   inner2 = outer1.getInner();
    		        Outer.Inner   inner3 = new Outer().getInner();
    		        
    		    }
    		}
    		class Outer{
    		    private int age;
    		    class Inner{
    		        public void funcInner(){
    		            System.out.println("成员内部类的方法");
    		        }
    		    }
    		    public void func(){
    		        Inner in=new Inner();
    		        in.funcInner();
    		    }
    		    public Inner getInner(){
    		        Inner inner=new Inner();
    		        return inner;
    		    }
    		}
    		
    
    
局部类

可以在方法体中定义类

局部内部类(有名字)
  • 可以访问外部类的所有成员

  • 不能添加访问修饰符,但是可以使用final 修饰符

  • 作用域仅在定义它的方法或代码块内

  • 当外部类和把内部类重名时,默认遵守就近原则;调用外部类属性的方法:外部类名.this.外部类属性名//谁调用该方法或代码块,则this指向谁

  • 
    public class Main {
        public void func(){
            class Inner{//内部类
                
            }
        }
    }
    
匿名内部类(没有名字)
  • 匿名内部类是在方法或代码块中编写一个内部类并创建一个实例
  • 且只能创建一个对象.匿名内部类使用一次,就不能再使用
  • 匿名并不是没有名字,系统会自动分配名字。
  • 不能添加访问修饰符,因为它的地位就是一个局部变量
  • 作用域:仅仅在定义它的方法或代码块中

		
		public class Test {
		    public static void main(String[] args) {
		        
		    }
		}
		class Outer{//外部类
		    private int n1 = 10;//属性
		    public void method() {//方法
		        //1. 匿名内部类是在方法或代码块中编写一个内部类并创建一个对象,
		        //   且只能创建一个对象.匿名内部类使用一次,就不能再使用
		        //2. 匿名并不是没有名字,系统会自动分配名字。
		        //3. 我们要根据它所实现的接口 或者 继承的类的名字来创建对象
		        //  ( 这里并不是根据接口或者抽象类来实例化对象)
		        //<1>基于实现接口的匿名内部类
		        /*
		            底层 会分配 类名 Outer04$1
		            class Outer$1 implements Interface1 {
		                @Override
		                public void fun() {
		                    System.out.println("fun");
		                }
		            }
		         */
		        Interface1 inner = new Interface1() {
		            @Override
		            public void fun() {
		                System.out.println("fun");
		            }
		        };
		        //调用1
		        inner.fun();
		        //调用2
		        new Interface1() {
		            @Override
		            public void fun() {
		                System.out.println("fun");
		            }
		        }.fun();
		
		        //查看对象的运行类型:  对象名.getClass()
		        System.out.println("inner的运行类型=" + inner.getClass());
		
		        //<2>基于父类的匿名内部类
		        /*
		            class Outer$2 extends A{
		                @Override
		                void fun1(){
		                    System.out.println("匿名内部类重写了父类");
		                }
		            }
		         */
		        A a =new A(){
		            @Override
		            public void fun1(){
		                System.out.println("匿名内部类重写了父类");
		            }
		        };//这是创建对象的语句,因此最后要有 ;
		
		        //基于抽象类的匿名内部类
		        B b =new B(){
		            public void fun2(){
		                System.out.println("匿名内部类定义了fun2");
		            }
		        };
		
		
		
		    }
		}
		interface Interface1{//接口
		    void fun();
		}
		class A {//类
		    public void fun1(){
		        System.out.println("父类的fun");
		    }
		}
		abstract class B{//抽象类
		    abstract void fun2();
		}

好处

  • 如果一个类只对另一个类有用,那么将它嵌入到该类中并将两个类放在一起是合乎逻辑的。
  • 更易读和维护
  • 提高了封装性

对象在内存中的形式

【零基础 快速学Java】韩顺平 零基础30天学会Java_哔哩哔哩_bilibili

在这里插入图片描述

在这里插入图片描述

  1. 加载类的信息,在方法区中,执行静态代码块,静态属性(只会执行一次)
  2. 在堆中分配空间,进行默认初始化,
  3. 执行普通属性,普通代码块,给属性显式初始化(创建一次,调用一次)
  4. 把地址赋给对象cat
  5. 执行构造器
  6. 进行指定初始化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8n7dvB7z-1676812627809)(imgs/image-20220513213213157.png)]

枚举

自定义枚举

public class Main2 {
  public static void main(String[] args) {
    //调用,这样不能自己new对象
    Season autumn = Season.AUTUMN;
  }
}
class Season{
  private String name;
  private String desc;
  //自定义枚举
  //枚举一般大写,static与final连用,优化底层机制
  public final static Season SPRING =new  Season("春天","温暖的");
  public final static Season WINTER =new  Season("冬天","寒冷的");
  public final static Season AUTUMN =new  Season("秋天","凉爽的");
  public final static Season SUMMER =new  Season("夏天","炎热的");
  //先将构造器私有化
  private Season(String name, String desc) {
    this.name = name;
    this.desc = desc;
  }
}

enum

public class Main2 {
    public static void main(String[] args) {
      //调用,这样不能自己new对象
      Season autumn = Season.AUTUMN;
    }
}
enum Season{//把class  换成enum
    /*
    1. 实例化常量放在第一行,多个常量中间用逗号间隔
    2. 具体格式如下所示  对象名(参数列表)
     */
    SPRING("春天","温暖的"),SUMMER("夏天","炎热的"),
    AUTUMN("秋天","凉爽的"),WINTER("冬天","寒冷的");
	
	//上面的权限和这句话是一样的,
	//public final static Season SPRING =new  Season("春天","温暖的");
	
    private String name;
    private String desc;
    //先将构造器私有化,enum默认的控制权限时 default
    private Season(String name, String desc) {
      this.name = name;
      this.desc = desc;
    }
  }

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值