Java语言基本元素:类和对象


类是对一类事物的描述,是抽象的、概念上的定义
对象是实际存在的该类事物的每个个体,因而也称为实例(instance)。

面向对象程序设计的重点是类的设计
类的设计, 其实就是类的成员的设计

常见的类的成员有:

修饰符 class 类名 {
属性声明;
方法声明;
}

修饰符:

  1. public 表明该类可以被任意类使用
  2. 什么也不写 表明该类只能被包内其它类使用
    类的正文要用{ }括起来

创建Java自定义类
步骤:
1. 定义类(考虑修饰符、类名)
2. 编写类的属性(考虑修饰符、属性类型、属性名、 初始化值)
3. 编写类的方法(考虑修饰符、返回值类型、方法名、形参等)

属性

语法格式:
修饰符 数据类型 属性名 = 初始化值 ;

image.png
说明1: 修饰符
常用的权限修饰符有: private、缺省、 protected、 public
其他修饰符: static、 final
说明2:数据类型
任何基本数据类型(如int、 boolean) 或 任何引用数据类型。
说明3:属性名
属于标识符,符合命名规则和规范即可。

成员变量VS局部变量

成员变量 局部变量
声明的位置 直接声明在类中 方法形参或内部、代码块内、构造器形参或内部等
修饰符 private、 public、 static、 final等 不能用权限修饰符修饰(相当于方法的权限
),可以用final修饰
初始化值 有默认初始化值 没有默认初始化值,必须显式赋值,方可使用
内存加载位置 堆空间 或 静态域内 栈空间

方法

什么是方法(method/函数):
方法是类或对象行为特征的抽象,用来完成某个功能操作。在某些语言中也称为函数或过程。
将功能封装为方法的目的是,可以实现代码重用,简化代码
Java里的方法不能独立存在,所有的方法必须定义在类里。

方法的声明格式:
修饰符 返回值类型 方法名(参数类型 形参1, 参数类型 形参2, ….){
方法体程序代码
return 返回值;
}

其中:
修饰符: public,缺省,private, protected等
返回值类型:
没有返回值: void(也能用return)。
有返回值,声明出返回值的类型。与方法体中“return 返回值(变量/常量)” 搭配使用
方法名:属于标识符,命名时遵循标识符命名规则和规范,“见名知意”
形参列表:可以包含零个,一个或多个参数。多个参数时,中间用“,”隔开
返回值:方法在执行完毕后返还给调用它的程序的数据。

方法调用:
方法被调用一次,就会执行一次
没有具体返回值的情况,返回值类型用关键字void表示,那么方法体中可以不必使用return语句。如果使用,仅用来结束方法。
定义方法时,方法的结果应该返回给调用者,交由调用者处理。
方法中只能调用方法或属性, 不可以在方法内部定义方法。

return关键字的使用

作用范围:仅仅在方法中
功能:结束方法/结束方法并返回值
注意:return后不能有其他执行语句,因为不可达!

方法重载:
重载的概念:
在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。
重载的特点:
与返回值类型无关,只看参数列表,且参数列表必须不同。 (参数个数或参数类型)。调用时, 根据方法参数列表的不同来区别。

可变个数的形参
JavaSE 5.0 中提供了Varargs(variable number of arguments)机制,允许直接定义能和多个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可变的实参
效果等同于数组(但传参方式不同,如果是数组的话只能传数组,但是可变参数可以传一个个该类型的值),不同的是数组作为形参之后仍然能写参数,而可变参数不能
判断重载时可以把可变长度参数看作该类型数组。

  1. //调用可变参数方法与数组参数方法的不同
  2. public static void main(String[] args)
  3. {
  4. //调用数组参数的方法
  5. show(new String[]{"aa", "bb", "cc"});
  6. //调用可变长度形参的方法
  7. show("aa", "bb", "cc");
  8. }
  9. //数组作为形参之后可以跟其他参数
  10. public void show(String[] arr, int num1)
  11. {
  12. for(int i=0;i<arr.length;i++)
  13. System.out.println(arr[i]);
  14. }
  15. //可变长度参数之后不能再有其他参数
  16. public void show(String ...strings)
  17. {
  18. }
  19. //下面两种方法同时写会报错,可变长度参数效果上等同于数组
  20. public void show(String[] arr)
  21. {
  22. for(int i=0;i<arr.length;i++)
  23. System.out.println(arr[i]);
  24. }
  25. public void show(String ...strings)
  26. {
  27. }
说明:
1. 声明格式: 方法名(参数的类型名 …参数名)
2. 可变参数:方法参数部分指定类型的参数个数是可变多个: 0个, 1个或多个
3. 可变个数形参的方法与同名的方法之间,彼此构成重载
4. 可变参数方法的使用与方法参数部分使用数组是一致的
5. 方法的参数部分有可变形参,需要放在形参声明的最后
6. 在一个方法的形参位置,最多只能声明一个可变个数形参

参数的值传递机制
方法,必须由其所在类或对象调用才有意义。
Java里方法的参数传递方式只有一种: 值传递。 即将实际参数值的副本
(复制品)传入方法内,而参数本身不受影响。
形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参
形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参

构造器

构造器的作用: 创建对象;给对象进行初始化

语法格式:
修饰符 类名 (参数列表) {
初始化语句;
}

构造器的特征
它具有与类相同的名称
它不声明返回值类型。(与声明为void不同)
构造器总是伴随new操作一起调用,不能通过类或对象之间调用
不能被static、 final、 synchronized、 abstract、 native修饰,不能有return语句返回值

根据参数不同,构造器可以分为如下两类:
隐式无参构造器(系统默认提供)
显式定义一个或多个构造器(无参、有参)

Java语言中,每个类都至少有一个构造器
默认构造器的修饰符与所属类的修饰符一致
一旦显式定义了构造器, 则系统不再提供默认构造器
一个类可以创建多个重载的构造器
父类的构造器不可被子类继承

构造器重载
构造器重载使得对象的创建更加灵活,方便创建各种不同的对象。
构造器重载,参数列表必须不同

代码块

代码块(或初始化块)的作用:对Java类或对象进行初始化
代码块(或初始化块)的分类:
一个类中代码块若有修饰符, 则只能被static修饰, 称为静态代码块(static block), 没有使用static修饰的, 为非静态代码块。

  1. static{
  2. //静态初始化语句
  3. }
  4. {
  5. //非静态初始化语句
  6. }

静态代码块:用static 修饰的代码块

  1. 可以有输出语句。
  2. 可以对类的属性、类的声明进行初始化操作。
  3. 不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。
  4. 若有多个静态的代码块,那么按照从上到下的顺序依次执行。
  5. 静态代码块的执行要先于非静态代码块。
  6. 静态代码块随着类的加载而加载,且只执行一次。

非静态代码块:没有static修饰的代码块

  1. 可以有输出语句。
  2. 可以对类的属性、 类的声明进行初始化操作。
  3. 除了调用非静态的结构外, 还可以调用静态的变量或方法。
  4. 若有多个非静态的代码块, 那么按照从上到下的顺序依次执行。
  5. 每次创建对象的时候, 都会执行一次。 且先于构造器执行。

内部类

当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。
在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类。
内部类一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称(外部类.内部类)。

内部类的类名不能与包含它的外部类类名相同;

分类:类似于变量的分类

  1. 成员内部类(static成员内部类和非static成员内部类)

4个权限修饰符,abstract,final都能用
static形式的内部类不能调用外部类成员方法或成员变量,但是能调用外部类的类变量和类方法。
非static形式的内部类既能调用外部类成员方法或成员变量,又能调用外部类的类变量和类方法。
创建内部类的实例:
静态的: 外部类.内部类 变量名 = new 外部类.内部类()
非静态的: 外部类.内部类 变量名 = 外部类对象.new 内部类()
非静态内部类调用外部类属性/方法的写法: 外部类.this.属性/方法
静态内部类只能调用外部类的非静态属性/方法

非static的成员内部类中的成员不能声明为static的, 只有在外部类或static的成员内部类中才可声明static成员。 外部类访问成员内部类的成员, 需要“内部类.成员”或“内部类对象.成员”的方式 当想要在外部类的静态成员部分使用内部类时, 可以考虑内部类声明为静态的 编译以后生成OuterClass$InnerClass.class字节码文件

如何声明成员内部类:

  1. class 外部类{
  2. static class 成员内部类{
  3. }
  4. class 成员内部类{
  5. }
  6. }
  1. 局部内部类(包括方法内,代码块内,构造器内)

不能用权限修饰符和static修饰符,只能用abstract或者final修饰
内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号,以及数字编号。

如何声明局部内部类:

  1. class 外部类{
  2. public 外部类(){ //构造器中
  3. class 局部内部类{
  4. //很少很少见
  5. }
  6. }
  7. 方法(){//方法中
  8. class 局部内部类{
  9. //开发中很少见这种,一般都是用匿名内部类
  10. }
  11. }
  12. {//代码块中
  13. class 局部内部类{
  14. //很少很少见
  15. }
  16. }
  17. }

如何使用局部内部类
只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类
但是它的对象可以通过外部方法的返回值返回使用,返回值类型只能是局部内部类的父类或父接口类型
局部内部类的特点
只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类。
局部内部类可以使用外部类的成员,包括私有的。
局部内部类可以使用外部方法的局部变量,但是必须是final的。 由局部内部类和局部变量的声明周期不同所致。
局部内部类和局部变量地位类似,不能使用public,protected,缺省,private
局部内部类不能使用static修饰,因此也不能包含静态成员

  1. 匿名内部类

匿名内部类不能定义任何静态成员、方法和类
只能创建匿名内部类的一个实例
一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
格式:

  1. new 父类构造器(实参列表) | 实现接口()
  2. {
  3. //匿名内部类的实体部分
  4. }

匿名内部类的特点
匿名内部类必须继承父类或实现接口
匿名内部类只能有一个对象
匿名内部类对象只能使用多态形式引用

对象的创建和使用

创建对象语法: 类名 对象名 = new 类名();
使用“对象名.对象成员”的方式访问对象成员(包括属性和方法)

类的访问机制:
在一个类中的访问机制: 类中的方法可以直接访问类中的成员变量。
(例外: static方法访问非static, 编译不通过。 )
在不同类中的访问机制: 先创建要访问类的对象, 再用对象访问中定义的成员。

匿名对象

我们也可以不定义对象的句柄,而直接调用这个对象的方法。这样的对象叫做匿名对象。
如:new Person().shout();
何时使用
如果对一个对象只需要进行一次方法调用,那么就可以使用匿名象。
我们经常将匿名对象作为实参传递给一个方法调用。

内存解析

image.png
堆( Heap) , 此内存区域的唯一目的就是存放对象实例, 几乎所有的对象实例都在这里分配内存。 这一点在
Java虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配。
通常所说的栈( Stack) , 是指虚拟机
栈。 虚拟机栈用于存储局部变量等。局部变量表存放了编译期可知长度的各种基本数据类型( boolean、 byte、
char 、 short 、 int 、 float 、 long 、double) 、 对象引用( reference类型,它不等同于对象本身, 是对象在堆内存的首地址) 。 方法执行完, 自动释放。
方法区(Method Area),用于存储已被虚拟机加载的类信息、 常量、 静态变量、 即时编译器编译后的代码等数据