类及类的成员
类和对象
对象的创建和类的实例化
首先要理解类和对象的关系,和JS类似,类是对象的抽象概念,对象是类的实例化
一个小例子
类的创建
在IDEA中提供了方便的创建类的方法,存放类的文件名就是类名
public class ClassName {
//属性
//方法
}
对象创建
由public修饰的类可直接被其他类访问,使用构造函数的方法进行类的实例化,获得一个对象
class Main {
public static void main(String[] args) {
Phone p = new Phone();
}
}
也可以创建一个匿名对象,直接访问方法和属性
new Phone().call();
new Phone().price;
通常作为参数传入方法
流程:
- 创建类
- 根据类创建对象
- 通过对象访问和修改类的属性和方法
对象的内存分析
- 堆:
- new出来的结构(对象、数组)都放在堆空间中。
- 对象的属性存放在堆空间中。
- 创建一个类的多个对象(比如p1、p2),则每个对象都拥有当前类的一套"副本"(即属性)。当通过一个对象修改其属性时,不会影响其它对象此属性的值。
- 当声明一个新的变量使用现有的对象进行赋值时(比如p3 = p1),此时并没有在堆空间中创建新的对象。而是两个变量共同指向了堆空间中同一个对象。当通过一个对象修改属性时,会影响另外一个对象对此属性的调用
- 栈:
- 存放创建的对象,对象中会存放地址,指向堆中存放数据
对象数组
数组的元素可以是对象,称为对象数组
创建方法和其他数组相同
Person[] p = new Person[20];
for (int i = 0; i < p.length; i++) {
p[i] = new Person();
}
但需要注意内部的每个元素还处于未初始化的状态,需要再次使用构造函数初始化,实际上访问方法也相同
for(Person per:p){
System.out.println(per.age + " " + per.weight + " " + per.name);
}
类的成员
属性
变量的分类
变量按数据类型可划分为基本数据类型和引用数据类型,下面提出另一种分类方法:
根据变量在类中声明位置的不同分为
- 成员变量 :属性
- 局部变量 :方法内、方法形参、构造器内
和C语言中的全局和局部类似
public class Phone {
double price = 100.0;// 成员变量
int amount = 100;// 成员变量
public void call() {
String number = "12345678";// 局部变量
System.out.println("Call" + number);
}
}
所以属性又称为成员变量
不同点:
- 作用域不同
- 内存中位置不同,由于方法存放在栈中,局部变量存放在栈中,而前面已经提到属性存放在堆中
- 属性可以使用权限修饰符进行修饰,局部变量不能修饰
- 属性都有默认初始化值,局部变量都没有初始化值
属性赋值的先后顺序
- 默认初始化
- 显式初始化或代码块中初始化
- 构造器中初始化
- 通过
对象.属性或对象.方法的方法进行赋值
方法
声明
格式:
权限修饰符 [其他修饰符] 返回值类型 方法名(形参列表)[throws 异常类型]{
// 方法体
}
方法的定义没有先后顺序
方法内不能定义方法
。。。实在没什么新东西。。。基础操作和C语言的函数逻辑基本一样
方法的重载
- 方法重载:在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可。
- 参数列表不同,意味着参数个数或参数类型的不同
- 重载的特点:与修饰符、返回值类型无关,只看参数列表,且参数列表必须不同。(参数个数或参数类型)。调用时,根据方法参数列表的不同来区别。
- 重载方法调用:JVM通过方法的参数列表,调用匹配的方法。先找个数、类型最匹配的再找个数和类型可以兼容的,如果同时多个方法可以兼容将会报错
方法重载说明可以定义多个同名方法,指定不同参数来实现不同参数的相同逻辑
一道面试题参考:判断println的输出
可变个数形参的方法(JDK5.0新特性)
格式:
方法名(参数的类型名 ...参数名)
特点:
- 可变参数:方法参数部分指定类型的参数个数是可变多个:0个,1个或多个
- 可变个数形参的方法与同名的方法之间,彼此构成重载
- 可变参数方法的使用与方法参数部分使用数组是一致的,二者不能同时声明,否则报错
即:
public void print(int... nums) {}
public void print(int[] nums) {}
二者使用方法相同,都是数组的使用,不同的是前者的调用不需要创建数组,依次传入参数即可:
PhoneTest p = new PhoneTest();
p.print(1, 2, 3, 4, 5);
- 方法的参数部分有可变形参,需要放在形参声明的最后
- 在一个方法的形参中,最多只能声明一个可变个数的形参
一个实例:使用可变个数形参拼接字符串
方法中的值传递机制
这里和C有所不同,相同的是基本数据类型传递的是值,引用数据类型传递的是地址,但是Java中无法获得基本数据类型的地址,只能放在对象中,传入对象,改变其内部的局部变量的值
构造器
类的成员之三
- 作用
- 搭配new关键字,创建类的对象
- 在创建对象的同时,可以给对象的相关属性赋值
- 使用:
权限修饰符 类名(形参列表){}
- 说明:
- 创建类以后,在没有显示提供任何构造器的情况下,系统会默认提供一个空参的构造器,且构造器的权限与类声明的权限相同
- 一旦类中显示声明了构造器,则系统不再提供默认的空参的构造器
- 一个类中可以声明多个构造器,彼此之间构成重载
在IDEA中,使用alt+insert快捷插入指定变量的构造器IDEA 常用快捷键一览表#第2组:提高编写速度(上)
代码块
可以有输出语句,可以对类的属性、类的声明进行初始化操作,多个代码块,按照从上到下的顺序依次执行
静态代码块
static修饰,并且只能使用static修饰
- 特点
- 不可以对非静态的属性初始化。即不可以调用非静态的属性和方法
- 静态代码块的执行要先于非静态代码块
- 静态代码块随着类的加载而执行,并且由于类只加载一次,静态代码块只执行一次
- 作用:初始化类的信息
public class BankTest {
public static void main(String[] args) {
System.out.println(Bank.info);
}
}
class Bank {
// 构造器私有化
String name;
int age;
static String info="OK";
{
System.out.println("Unstatic");
name = "Laoba";
age = 18;
}
static {
System.out.println("Static");
}
}
输出为:
Static
OK
不会输出Unstatic
非静态代码块
- 特点
- 除了调用非静态的结构外,还可以调用静态的变量或方法
- 随着对象创建而执行,每次创建对象的时候,都会执行一次。且先于构造器执行
- 作用:初始化对象的信息
public class BankTest {
public static void main(String[] args) {
Bank instance = new Bank();
}
}
class Bank {
// 构造器私有化
String name;
int age;
static String info = "OK";
{
System.out.println("Unstatic");
name = "Laoba";
age = 18;
}
static {
System.out.println("Static");
}
}
输出为:
Static
Unstatic
应用
多个构造器的相同逻辑可以放在非静态代码块中执行
{
System.out.println("新用户注册");
registrationTime = System.currentTimeMillis();
//获取系统当前时间 (距离1970-1-1 00:00:00的毫秒数)
}
//代码块的使用
public User1(){
// System.out.println("新用户注册");
// registrationTime = System.currentTimeMillis();
userName = System.currentTimeMillis() + "";
password = "123456";
}
public User1(String userName,String password){
// System.out.println("新用户注册");
// registrationTime = System.currentTimeMillis();
this.userName = userName;
this.password = password;
}
内部类
练习题:练习题#内部类
将一个类A定义在另一个类B里面,里面的那个类A就称为 内部类(InnerClass),类B则称为 外部类(OuterClass)
具体来说,当一个事物A的内部,还有一个部分需要一个完整的结构B进行描述,而这个内部的完整的结构B又只为外部事物A提供服务,不在其他地方单独使用,那么整个内部的完整结构B最好使用内部类
总的来说,遵循 高内聚、低耦合的面向对象开发原则
分类
类似于变量的分类
成员内部类
直接声明在外部类的里面,代码块、方法中
- static修饰:静态的成员内部类
- 不使用static修饰:非静态的成员内部类
理解
- 从类的角度:
- 内部可以声明属性、方法、构造器、代码块、内部类等结构
- 此内部类可以声明父类,可以实现接口
- 可以使用final修饰
- 可以使用abstract修饰
- 从外部类的成员的角度看:
- 在内部可以调用外部类的结构。比如:属性、方法等
- 除了使用public、缺省权限修饰之外,还可以使用private、protected修饰
- 可以使用static修饰
创建成员内部类的实例
class Person {
String name;
static class Brain {}
class Intelligence {}
}
- 静态内部类:
Person.Brain brain = new Person.Brain();
即静态的成员可以直接被类访问
- 非静态的内部类:
Person p1 = new Person();
Person.Intelligence i1= p1.new Intelligence();
非静态只能由实例调用,所以肯定要创建外部类的实例,但是内部类的实例创建写法有些特殊
访问外部类的结构
class Person {
String name = "Perosn";
public void love(){
System.out.println("Love");
}
class Intelligence {
String name = "Intelligence";
public void fun() {
System.out.println(name);// Intelligence
System.out.println(this.name);// Intelligence
System.out.println(Person.this.name);// Person
Person.this.love();// Love
}
}
}
局部内部类
非匿名局部内部类
匿名局部内部类
定义在方法等类的结构中
格式:
[修饰符] class 外部类{
[修饰符] 返回值类型 方法名(形参列表){
[final/abstract] class 内部类{
}
}
}
基本使用:
public class OuterClassTest1 {
//开发中的场景
public Comparable getInstance(){
//提供了实现了Comparable接口的类
//方式1:提供了接口的实现类的对象
class MyComparable implements Comparable{
@Override
public int compareTo(Object o) {
return 0;
}
}
MyComparable m = new MyComparable();
return m;
//方式2:提供了接口的实现类的匿名对象
class MyComparable implements Comparable{
@Override
public int compareTo(Object o) {
return 0;
}
}
return new MyComparable();
//方式3:提供了接口的匿名实现类的对象
Comparable c = new Comparable(){
@Override
public int compareTo(Object o) {
return 0;
}
};
return c;
//方式4:提供了接口的匿名实现类的匿名对象
return new Comparable(){
@Override
public int compareTo(Object o) {
return 0;
}
};
}
}