类与对象
这是一个目录
1 概念
1.1 类与对象的关系
类:抽象的,概念的,是自定义数据类型,可以定义类的各种程序变量(属性, 或称field,字段)和方法
对象:实际的,一个具体的实例
类是对象的模版,对象是类的一个个体,对应一个实例
1.2 类的定义
//定义
class ClassName{//class为定义类的关键字
field;//属性
method;//成员方法
}
//创建对象
//实例化:用类类型创建对象的过程,称为类的实例化,创建时使用关键字new
//1.直接创建
ClassName objectName = new ClassName();
//2.先声明再创建
ClassName objectName;
objectName = new ClassName();
//访问属性
objectName.field;
//访问方法
objectName.method;
1.3 内存分配
栈:一般存放基本数据类型(局部变量)
堆:存放对象(类、数组等)
方法区:常量池(常量,比如字符串),类加载信息(属性信息、方法信息)
2 属性(成员变量)
属性是类的一个组成部分,一般是基本数据类型,也可以是引用类型(对象,数组等)
//示例:访问修饰符 属性类型 属性名
private int a;
//也可以直接赋值
private int a = 1;
注意事项
-
属性的定义类型可以为任意类型,包含基本类型或引用类型
-
属性如果不赋值,则有默认值
3 成员方法
许多情况下,类中需要定义成员方法
3.1 成员方法的定义
访问修饰符 返回数据类型 方法名 (形参列表) {//方法体
语句;
return 返回值;
}
//形参列表和return语句非必需
3.2 方法的调用
定义的方法存放常量区
1)当程序执行到方法时,就会开辟一个独立的空间(栈空间),在空间内创建变量,方法的局部变量是独立的,不会互相影响
2)当方法执行完毕,或者执行道return语句时,就会返回至调用方法的语句
4)返回后,继续执行方法后面的代码
5)当main方法(栈)执行完毕,整个程序退出
注意事项
-
访问修饰符控制方法使用的范围
private,default(即不写),protected,public -
一个方法的返回值最多只能有一个(想返回多个值可以使用数组),返回数据类型可以为任意类型,包含基本类型或引用类型(数组、对象)
-
如果方法要求有返回数据类型(即非void),则最后方法中的最后执行语句必须为return值(返回值的类型应与要求的一致)
-
遵循驼峰命名法,见名知义(变量名小驼峰类名大驼峰)
4 作用域
- Java中主要的变量就是属性(成员变量)和局部变量
- 局部变量一般指在成员方法中定义的变量
- Java中全局变量(即属性)的作用域为整个类体,局部变量(除属性外的其他变量)的作用域为定义它的代码块中
- 全局变量(属性)可以不赋值直接使用,因为有默认值,局部变量必须赋值后才能使用,因为没有默认值
注意事项
-
属性和局部变量可以重名,访问时遵循就近原则
-
在同一个作用域中,比如在同一个成员方法中,两个局部变量不能重名
-
属性生命周期较长,伴随着对象的创建而创建和销毁而销毁。局部变量生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而销毁,即一次方法调用。
-
作用域范围不同
全局变量/属性:可以被本类或其他类使用(通过对象调用)
局部变量:只能在本类中对应的方法中使用 -
修饰符不同:全局变量/属性可以加修饰符,局部变量不可以加修饰符
5 构造方法(构造器)
构造方法允许在创建对象时,直接将对像的属性初始化
例:
//语法
class 类名{
[修饰符] 方法名 (形参列表){
方法体;
}
}
class Person{
String name;
int age = 90;
Person(String n, int a){
name = n;
age = a;
}
Person p = new Person("小明", 20);
}
注意事项
-
构造方法名要与类名相同
-
构造方法没有返回值
-
在创建对象时,系统自动调用该类的构造方法(只调用一次):若用户没有定义构造方法,系统调用默认的无参构造方法,否则调用定义的构造方法(此时默认构造方法被覆盖)
-
一个类可以定义多个不同的构造器,即构造器重载
6 this 关键字
构造器的形参为了方便一般直接写成参数名。如下:
public class Test {
public static void main(String[] args) {
Person p = new Person("小明", 20, "翻斗花园");
}
}
class Person{
String name;
int age;
String address;
public Person(String name, int age, String address){
this.name = name;
this.age = age;
this.address = address;
}
//构造器中的name,age,address基于就近原则编译器将其理解为局部变量,而不是属性
//局部变量在Person构造方法外则无法作用,所以info无法有效输出目标的结果。
public void info(){
System.out.println(name + "\t" + age + "\t" + address);
}
}
解决这个问题只需在构造方法中的局部变量前加关键字this
即:
public Person(String name, int age, String address;){
this.name = name;
this.age = age;
this.address = adderss;
}
表示当前对象的属性 name 和 age,哪个对象调用,this 就代表哪个对象
注意事项
-
this关键字可以用来访问本类的属性、方法、构造器
-
this 用于区分当前类的属性和局部变量
-
访问成员方法的语法:this.方法名(参数列表);
-
访问构造器语法:this(参数列表);
注意只能在构造器中使用(this关键字的语句必须置于构造方法中的第一条语句)
-
this不能在类定义的外部使用,只能在类定义的方法中使用
-
this是成员方法的第一个隐藏参数
在上述代码中
public void info(){
System.out.println(name + "\t" + age + "\t" + address);
}
在属性前加上this
public void info(){
System.out.println(this.name + "\t" + this.age + "\t" + this.address);
}
调用该方法,两种写法运行结果均为
7 修饰符
Java提供四种访问控制修饰符号控制方法和属性(成员变量)的访问权限(范围)
1.public:公开级别,对外公开
2.protected:受保护级别,对子类和同一个包中的类公开
3.默认:没有修饰符号,向同一个包的类公开
4.private:私有级别,只有类本身可以访问,不对外公开
访问级别 | 修饰符 | 同类 | 同包 | 子类 | 不同包 |
---|---|---|---|---|---|
公开 | public | √ | √ | √ | √ |
受保护 | protected | √ | √ | √ | × |
默认 | √ | √ | × | × | |
私有 | private | √ | × | × | × |
注意事项
- 修饰符可以用来修饰类中的属性,成员方法以及类
- 子类可以重写父类中被protected修饰的方法,且可以放大可访问的权限,即改写成public,但不能将父类中的public修饰方法改为protected,即缩小访问的权限
- 只有默认和public才能修饰类,并遵循上述访问权限的特点
- 成员方法的访问规则和属性一致
8 包
包是Java实现面向对象的封装机制的一种体现,程序员可以建立包来实现对多个类的管理
包的作用
1.区分相同名字的类
2.当类很多时,可以很好的管理类
3.控制访问范围
基本语法
package name;
//1.package: 关键字,表示打包
//2.name: 表示包名
//规则:包名只能包含数字、字母、下划线、点,但不能用数字开头,不能是关键字或保留字
包的本质
实际上是创建不同的文件夹来保存类文件
注意事项
- package 的作用是是声明当前类所在的包,需要放在类的最上面,一个类中最多只有一条package
- import指令,用于导入包,位置放在package的下面,在类定义前面,可以有多条且没有顺序要求
9 static 关键字
static关键字可以修饰成员变量,成员方法和代码块
static修饰成员变量
当static修饰成员变量时,该成员变量变为静态变量,静态变量可用于引用所有对象的公共属性。
class Person{
String name;
int age;
String address;
static int money = 100;//money为静态变量
public Person(String name, int age, String address){
this.name = name;
this.age = age;
this.address = address;
}
public void info(){
System.out.println(name + "\t" + age + "\t" + address + "\t" + money);
money++;
}
}
public class Test {
public static void main(String[] args) {
Person p = new Person("小明", 20, "翻斗花园");//实例化p对象,money此时为100
p.info();//调用成员方法,money + 1,此时money = 101
Person q = new Person("小王", 20, "翻斗花园");//实例化q对象,money此时为101
}
}
static修饰成员方法
被static修饰的成员方法称静态成员方法,静态方法属于类,是类的方法,而不属于类的对象,不是某个具体对象的方法
-
静态方法可无需实例化即可直接调用
-
静态方法可以访问静态属性,即静态成员变量
-
静态方法不能访问非静态成员和调用非静态方法
//以上述代码为例
public static void info(){//info()为static修饰的静态方法
System.out.println(name + "\t" + age + "\t" + address + "\t" + money);
//编译时报错,原因时除money外,其余三个变量为非静态成员
money++;
}
//修正后,可直接调用info()
public class Test {
public static void main(String[] args) {
Person.info();
}
}
static修饰代码块
代码块{}前加static,即为静态代码块,一般用于初始化静态成员变量
class Person{
//实例成员
String name;
int age;
String address;
//静态成员
static int money;//money为静态变量
//实例代码块
{
System.out.println("实例代码块运行");
name = "小明";
age = 20;
address = "翻斗花园";
}
//静态代码块
static {
System.out.println("静态代码块运行");
money = 1000;
}
public Person() {
System.out.println("无参构造器运行");
}
public Person(String name, int age, String address){
this.name = name;
this.age = age;
this.address = address;
}
public static void info1(){
money++;
System.out.println(money);
System.out.println("静态方法调用");
}
public void info2(){
//普通代码块
{
System.out.println("普通代码块运行");
System.out.println(name + "\t" + age + "\t" + address + "\t" + money);
}
money++;
}
}
public class Test {
public static void main(String[] args) {
System.out.println("============");
Person p = new Person();
System.out.println("============");
Person.info1();
System.out.println("============");
p.info2();
}
}
运行结果如下:
由运行结果可见,程序执行的顺序为先加载静态成员,执行静态代码块中的语句完成静态成员的初始化,再执行实例化代码块和构造方法中的语句,静态方法info1()和成员方法info2()中普通代码块则需待main中相应语句执行调用
10 内部类
内部类可以在类中的任意位置定义,内部类可分为三种:成员内部类、局部内部类、匿名内部类
成员内部类
成员内部类又可分为实例内部类和静态内部类
class Person{
String name = "小明";
int age;
String address;
static int money = 100;
public Person(){
//无参构造器
}
public Person(String name, int age, String address){
this.name = name;
this.age = age;
this.address = address;
}
public void info1(){
System.out.println("外部类实例方法");
}
public static void info2(){
System.out.println("外部类静态方法");
}
//实例内部类
class Student{
String name = "学生";
String id = "1234567";
public void info3(){
info1();
info2();
System.out.println("实例内部类方法");
System.out.println("实例内部类属性:" + name + " " + id);
System.out.println("外部类非静态属性:"+ age +
"\t外部类静态属性:" + money);
}
}
//静态内部类
static class teacher {
String name = "老师";
String id = "9876543";
void info4(){
info2();
System.out.println("静态内部类方法");
System.out.println("静态内部类属性" + name + " " + id);
System.out.println("外部类静态属性:" + money);
}
}
}
public class Test {
public static void main(String[] args) {
System.out.println("======实例内部类初始化======");
Person.Student p = new Person("小明", 20, "翻斗花园").new Student();
p.info3();
System.out.println("======静态内部类初始化======");
Person.teacher q = new Person.teacher();
q.info4();
}
}
运行结果如下:
注意事项
-
当内部类和外部类有重名属性时,优先读取内部类属性
-
实例内部类成员方法可以读取调用外部类的任意成员
-
静态内部类成员方法只能访问外部类的静态成员
-
实例内部类可受修饰符的约束
局部内部类
定义在外部类的方法体或者{}中,该种内部类只能在其定义的位置使用,使用的非常少。
匿名内部类
匿名内部类没有名称,无构造函数。匿名内部类是一次性类,定义时同时创建它的对象,具体后续博客补充。