面向:拿、找
对象:能干活的东西
面向对象编程:拿东西过来做对应的事情
面向对象编程的例子:
import java.util.Random;
import java.util.Scanner;
public class mian {
public static void main(String[] args) {
//面向对象,导入一个随机数
Random r = new Random();
int data = r.nextInt(10)+1;
//面向对象,输入一个随机数
System.out.println(data);
Scanner sc = new Scanner(System.in);
// 面向对象,输出一个数
System.out.println("请输入一个数:");
int a = sc.nextInt();
System.out.println(a);
}
}
为什么java要采取这种方法来编程呢?
我们在程序之中要干某种事,需要某种工具来完成,这样更符合人类的思维习惯,编程更简单,更好理解。
面向对象的重点学习对象是什么? 学习获取已有对象并使用,学习如何自己设计对象并使用。——面向对象的语法
类(设计图):是对象共同特征的描述
如何定义类:
public class 类名{
1.成员变量(代表属性,一般是名词)
2.成员方法(代表行为,一般是动词)
3.构造器(后面学习)
4.代码块(后面学习)
5.内部类(后面学习)
}
public class Phone{
//属性(成员变量)
String brand;
double price;
public void call(){
}
public void playGame(){
}
}
如何得到对象?
如何得到类的对象:
类名 对象名= new 类名();
Phone p = new Phone();
对象:是真实存在的具体东西
拿到对象后能做什么?
对象.成员变量;
对象.成员方法(...)
在JAVA中,必须先设计类,才获得对象
public class phone {
//属性
String name;
double price;
public void call(){
System.out.println("打电话");
}
public void send(){
System.out.println("发短信");
}
}
//测试
public class phoneTest {
public static void main(String[] args) {
//创建手机对象
phone p = new phone();
//给手机对象赋值
p.name = "小米";
p.price = 1999;
//获取手机对象的属性值
System.out.println(p.name);
System.out.println(p.price);
//调用手机对象的方法
p.call();
p.send();
}
}
描述
一类事物的类,专业叫做:Javabean类
。
在javabean类中,是不写main方法的。
main方法
的类,叫做测试类
。
我们可以在测试中创建javabean类的对象并进行赋值调用。
public class 类名 {
1.成员变量(代表属性)
2.成员方法(代表行为)
}
public class Student {
//属性(成员变量)
String name;
int age;
//行为方法
public void study(){
System.out.println("好好学习,天天向上");
}
public void doHomework(){
System.out.println("键盘敲烂,月薪过万");
}
}
类名首字母建议大写
,需要见名知意,驼峰模式。
一个java文件中可以定义多个class类,且只能一个类是public
修饰的类名必须成为代码文件名。
实际开发中建议还是一个文件定义一个class类。
成员变量的完整定义格式是:修饰符
数据类型
变量名称
=初始化值
;一般无需指定初始化值,存在默认值。
int age;
//这里不写初始化值是因为,这里学生的年龄是一个群体的值,没有一个固定的初始化值。
//如果给age赋值,比如是18岁,那就代表者所有的学生年龄都是18岁。
//类的赋值不是在类里面赋值,而是在创建了对象之后再赋值,这时赋值的时这个特定的对象。
Student stu = new Student();
Stu.name="张三";
Stu.height=187;
对象的成员变量的默认值规则
数据类型 | 明细 | 默认值 |
---|---|---|
基本类型 | byte,short,int,long | 0 |
基本类型 | float,double | 0.0 |
基本类型 | boolean | false |
引用类型 | 类、接口、数组、String | null |
//编写女朋友类,创建女朋友类的对象,给女朋友的属性赋值并调用女朋友类中的方法。自己思考女朋友有哪些属性,有哪些行为?
public class girlFriend {
public static void main(String[] args) {
//创建女朋友对象
girl g = new girl();
//给女朋友对象赋值
g.name = "小红";
g.age = 20;
g.hobby = "唱歌";
//获取女朋友对象的属性值
System.out.println(g.name);
System.out.println(g.age);
System.out.println(g.hobby);
//调用女朋友对象的方法
g.eat();
g.sleep();
}
}
//这是一个类
public class girl {
//成员变量(代表属性)
String name;
int age;
String hobby;
//成员方法(代表行为)
public void eat(){
System.out.println("吃饭");
}
public void sleep(){
System.out.println("睡觉");
}
}
先把需求拿过来,先要看这个需求当中有几类事物。每个事物,每类事务都要定义为单独的类,这类事物的名词都可以定义为属性,这类事物的功能,一般是动词,可以定义为行为。
封装是面向对象的三大特征:封装、继承、多态
封装的作用:告诉我们,如何正确设计对象的属性和方法。
/**需求:定义一个类描述人
属性:姓名、年龄
行为:吃饭、睡觉*/
public class Person{
String name;
int age;
public void eat(){
System.out.println("吃饭");
}
public void sleep(){
System.out.println("睡觉");
}
}
原则:对象代表什么,就得封装对应的数据,并提供数据对应的行为。
public class Circle {
double radius;
public void draw(){
System.out.println("根据半径"+radius+"画圆");
}
}
//人画圆,我们通常人为行为主体是人,其实是圆
//例如:人关门,这个门一定是门自己关的,人只是给了作用力,是门自己关上的。
是一个权限修饰符
可以修饰成员(成员变量和成员方法)
被private修饰的成员只能在本类中才能访问
public class GirlFriend{
private String name;
private int age;
private String gender;
}
public class leiMing {
private int age;
//set(赋值)
public void setAge(int a){
if(a<0||a>120){
System.out.println("你给的年龄有误");
return;
}
age = a;
}
//get(取值)
public int getAge(){
return age;
}
}
针对private修饰的成员变量,如果需要被其他类使用,提供相应的操作
提供“setXxx(参数)
”方法,用于给成员变量复制,方法用public修饰
提供“getXxx()”
方法,用于获取成员变量的值,方法用public修饰
为什么要调用set和get呢?
封装是面向对象编程的四大特性之一,它将数据(成员变量)和操作数据的方法绑定在一起,并隐藏对象的内部实现细节。通过将成员变量声明为 private
,外部类无法直接访问和修改这些变量,只能通过类提供的 set
和 get
方法来间接操作。这样可以防止外部代码对数据进行非法或不恰当的修改,保证数据的安全性和完整性。
public class GirlFriend{
private int age;//成员变量:方法的外面,类的里面
public void method(){
int age = 10;//局部变量:方法的里面
System.out.println(age);
}
}
成员变量和局部变量一致时,采用就近原则
谁离我近,我就用谁
public class GirlFriend{
private int age;//成员变量:方法的外面,类的里面
public void method(){
int age = 10;//局部变量:方法的里面
System.out.println(age);
}
}
//在这里中,最后1个age距离 age=10最近,所以最后一个age用的是10的值
//假如我想用第一个int ,我们可以在System.out.println(this.age)
age前加入:this. 这里就可以打破就近原则,选择另一个变量
在 Java 中,当局部变量(比如方法的参数)和类的成员变量重名时,就会产生命名冲突。在这种情况下,如果直接使用变量名,Java 默认会使用局部变量。而 this
关键字的一个重要作用就是用来引用当前对象的成员变量,从而区分局部变量和成员变量。
下面通过一个简单的示例来详细讲解从引用成员变量方向 this
关键字的用法:
class Employee {
// 定义成员变量
private String name;
private int age;
// 构造方法,用于初始化员工信息
public Employee(String name, int age) {
// 这里参数名和成员变量名相同,使用 this 引用成员变量
this.name = name;
this.age = age;
}
// 设置员工姓名的方法
public void setName(String name) {
// 使用 this 引用成员变量
this.name = name;
}
// 获取员工姓名的方法
public String getName() {
return this.name;
}
// 设置员工年龄的方法
public void setAge(int age) {
// 使用 this 引用成员变量
this.age = age;
}
// 获取员工年龄的方法
public int getAge() {
return this.age;
}
// 显示员工信息的方法
public void displayInfo() {
System.out.println("姓名: " + this.name + ", 年龄: " + this.age);
}
}
public class ThisKeywordVariableExample {
public static void main(String[] args) {
// 创建一个 Employee 对象
Employee employee = new Employee("李四", 25);
// 调用 displayInfo 方法显示员工信息
employee.displayInfo();
// 调用 setName 和 setAge 方法修改员工信息
employee.setName("王五");
employee.setAge(30);
// 再次调用 displayInfo 方法显示修改后的员工信息
employee.displayInfo();
}
}
private String name;
private int age;
这里定义了两个私有成员变量 name
和 age
,用于存储员工的姓名和年龄。
this
关键字使用public Employee(String name, int age) {
this.name = name;
this.age = age;
}
在构造方法中,参数名 name
和 age
与类的成员变量名相同。此时,this.name
表示当前对象的成员变量 name
,而直接使用的 name
则是构造方法的参数(局部变量)。通过 this.name = name;
语句,将局部变量 name
的值赋给了当前对象的成员变量 name
。同理,this.age = age;
也是将局部变量 age
的值赋给了成员变量 age
。
set
方法中的 this
关键字使用public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
在 setName
和 setAge
方法中,同样存在参数名和成员变量名相同的情况。使用 this
关键字来明确指定要操作的是当前对象的成员变量,避免了与局部变量的混淆。
get
方法中的 this
关键字使用public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
在 get
方法中,使用 this.name
和 this.age
来返回当前对象的成员变量的值。虽然在这种情况下,不使用 this
关键字也可以正常返回成员变量的值,因为这里没有局部变量与成员变量重名的问题,但使用 this
可以使代码的意图更加清晰,表明是在访问当前对象的成员变量。
displayInfo
方法中的 this
关键字使用public void displayInfo() {
System.out.println("姓名: " + this.name + ", 年龄: " + this.age);
}
在 displayInfo
方法中,使用 this.name
和 this.age
来获取当前对象的成员变量的值,并将其输出。
构造方法也叫做构造器、构造函数
public class Student{
修饰符 类名(参数){
方法体;
}
}
public class Student {
private String name;
private int age;
//如果我们自己没有写构造方法
// 那么编译器会自动生成一个无参构造方法
public Student() {
System.out.println("无参构造方法");
}
public Student(String name, int age) {
this.name = name;
this.age = age; //有参构造方法
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class StudentTest {
public static void main(String[] args) {
//创建类的对象
//调用的空参构造
//Student s1 = new Student();
Student s = new Student(name:"张三", age:20);
System.out.println(s.getName());
System.out.println(s.getAge());
}
}
特点:
大小写
也要一致void
都没有返回值
(不能由return带回结果数据)执行时机:
虚拟机
调用,不能手动调用构造方法在创建对象的时候,由虚拟机自动调用构造方法,作用是给成员变量进行初始化的
public class Student{
private String name;
private int age;
public Student(){
...//空参构造方法
}
public Student (String name, int age){
....//带全部参数构造方法
}
}
无参构造方法:初始化的对象时,成员变量的数据均采用默认值
有参构造方法:在初始化对象的时候,同时可以为对象进行
private
修饰setXxx()/getXxx()
举例子:
根据一个登录界面写一个JavaBean类
public class User {
//属性
private String username;
private String password;
private String email;
private String gender;
private int age;
//构造方法
//无参构造
public User() {
}
//有参构造
public User(String username, String password, String email, String gender, int age) {
this.username = username;
this.password = password;
this.email = email;
this.gender = gender;
this.age = age;
}
//方法
//set和get方法
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
我们再写一个javabean中会遇到一个问题:
这样写纯体力活啊!没事的没事的!我们有快捷键:
方法一:
alt+insert 或 alt+insert+Fn
alt+insert 第一个是构造函数,点击无选择生成的是空参 ,全选ok生成的是有参数的构造函数
alt+insert 点击setter和geteer,全选生成的是set和get
方法二:
下载插件pdg,下载完成后点击空白处就会出现。然后点击Ptg To JavaBean
Student s = new Student();
举例:
public class Student{
String name;
int age;
public void study(){
System.out.println("好好学习")
}
}
public class TestStudent{
public static void main(String [] args){
Student s= new Student();
System.out.println(s);
System.out.println(s.name+"...."+s.age);
s.name = "阿强";
s.age = 23;
System.out.println(s.name+"..."+s.age);
s.study();
}
}
解析:
加载class文件
JVM将
Student.class
和
TestStudent.class
加载到方法区,存储类结构信息(字段、方法签名、常量池等)。
Student
类包含字段name
(String)、age
(int)和方法study()
。TestStudent
类包含main()
方法入口。main()
时,在栈内存中创建main
方法的栈帧,声明局部变量s
(此时s
未指向任何对象,值为null
)。new Student()
时,在堆内存中为Student
对象分配空间,内存大小由字段类型决定(String
引用 + int
值)。name
→ null
(引用类型默认值)age
→ 0
(基本类型默认值)。String name = "默认";
),此时会执行。但示例代码未定义,此步骤跳过。public Student() { age = 18; }
),会通过构造器赋值。示例代码未定义构造方法,此步骤跳过。地址赋值给局部变量
堆中对象地址赋值给栈帧中的
s
变量,完成引用关联。
Student s = new Student();
后,s
指向堆内存中的对象。对象字段修改
后续代码通过s.name = "阿强";
和s.age = 23;
直接修改堆中对象的字段值,无需重新初始化。
s.study()
study()
的字节码指令。study()
方法的栈帧,执行System.out.println(" 好好学习")
(注:用户代码此处缺少分号,实际会编译报错)。步骤 | 操作内容 | 内存区域 | 示例代码体现 |
---|---|---|---|
1 | 加载类信息 | 方法区 | Student和TestStudent类加载 |
2 | 声明局部变量s | 栈内存 | Student s; |
3 | 堆中分配对象空间 | 堆内存 | new Student() |
4 | 字段默认初始化(null/0) | 堆内存 | s.name 和s.age 初始值 |
5 | 显式/构造初始化(无) | - | 代码未定义相关逻辑 |
6 | 对象地址赋值给s | 栈内存 | s = new Student(); |
7 | 修改字段值 | 堆内存 | s.name = "阿强";等操作 |
System.out.println(s)
输出哈希值
因打印对象时默认调用toString()
,而Student
未重写该方法,输出格式为类名@哈希值
。s
修改堆中对象字段,所有指向该对象的引用均会看到更新后的值。study()
方法中System.out.println(" 好好学习")
缺少分号,实际运行前会因语法错误中断。举例:
public class Student{
String name;
int age;
public void study(){
System.out.println("好好学习");
}
}
public class TestStudent{
public static void main(String [] args){
Student s= new Student();
System.out.println(s);
s.name = "阿强";
s.age = 23;
System.out.println(s.name+"..."+s.age);
s.study();
Student s2= new Student();
System.out.println(s2);
s2.name = "阿珍";
s2.age = 24;
System.out.println(s2.name+"..."+s2.age);
s2.study();
}
}
第二次在创建对象时。class文件不需要再加载一次
解析:
Student
类时。Student.class
加载到方法区,存储类结构(字段name
、age
和方法study()
的定义)。TestStudent.class
加载到方法区,存储main()
方法入口。main()
方法时,在栈内存创建main
方法的栈帧。s
和s2
(初始值均为null
)。new Student()
触发堆内存分配,根据类结构计算对象大小(String
引用 + int
值)。s = new Student()
→ 堆地址0x001
。s2 = new Student()
→ 堆地址0x002
(独立空间)。name
→ null
(引用类型默认值)。age
→ 0
(基本类型默认值)。s
的初始状态:name=null
, age=0
。s2
的初始状态:name=null
, age=0
。String name = "默认"
)。Student
类未定义显式赋值字段,跳过此步骤。public Student() { ... }
)。Student
类未定义构造方法,使用默认无参构造器,跳过此步骤。s = 0x001
(指向第一个对象)。s2 = 0x002
(指向第二个对象)。对象 | 堆地址 | 字段修改后的值 |
---|---|---|
s | 0x001 | name="阿强", age=23 |
s2 | 0x002 | name="阿珍", age=24 |
s
和s2
指向不同堆地址,修改其中一个对象的字段不会影响另一个对象。System.out.println(s == s2)
→ 输出false
。阶段 | 操作内容 | 内存区域 |
---|---|---|
类加载 | 加载Student和TestStudent类信息 | 方法区 |
栈帧创建 | 声明s和s2(初始null) | 栈内存 |
堆内存分配 | 为s和s2分配独立空间 | 堆内存 |
对象初始化 | 默认初始化 → 显式赋值(用户代码修改) | 堆内存 |
方法调用 | study()从方法区加载逻辑到栈执行 | 栈内存 |
System.out.println(s)
输出地址?toString()
方法,默认调用Object.toString()
,格式为类名@哈希值
。String name = "张三"
),编译时自动插入到构造方法中。new
对象(尤其循环中)。main()
结束后,s
和s2
成为垃圾对象,由GC自动回收。附:修正后的代码输出示例
Student@1b6d3586
阿强...23
好好学习
Student@4554617c
阿珍...24
好好学习
举例:
public class Student{
String name;
int age;
public void study(){
System.out.println("好好学习");
}
}
public class TestStudent{
public static void main(String [] args){
Student s= new Student();
s.name = "阿强";
Student s2= s;
s2.name = "阿珍";
System.out.println(s.name+"..."+s2.name);
}
}
TestStudent.class
加载到方法区,存储类结构信息(成员方法、字段描述等)new Student()
时触发类加载机制,将Student.class
加载到方法区,包含name
、age
字段和study()
方法元数据s
(地址未初始化)和s2
(此时两者均为null
)new Student()
操作码触发堆内存分配,此时: String name
+ int age
)name=null
,age=0
(基本类型和引用类型的零值初始化)String name = "默认名"
),此阶段跳过invokespecial
指令调用<init>
方法完成初始化s
变量(完成s = new Student()
)s2 = s
操作使s2
指向堆中同一个对象(此时两个引用共享对象数据)s2.name = "阿珍"
修改堆内存对象数据,此时s.name
同步变化(引用指向同一实体)内存区域 | 存储内容 |
---|---|
方法区 | TestStudent类字节码、Student类元数据(包含study()方法代码) |
堆内存 | Student对象实例(name=“阿珍”, age=0) |
栈内存 | main方法栈帧:s=0x100(指向堆对象), s2=0x100(与s同地址) |
System.out.println(s.name+"..."+s2.name)
→ 输出阿珍...阿珍
(s与s2引用同一对象,堆内数据修改对所有引用可见)
关键理解点:引用类型变量的赋值操作传递的是对象地址值,而非创建新对象。这种特性是Java对象共享机制的核心体现。
public class Student{
private int age;
public void method(){
int age=10;
System.out.println(age);//10
System.out.println(this.age);//成员变量的值 0
}
}
this的作用
:区分局部变量和成员变量
this的本质
:所在方法调用者
的地址值
public class Student{
private int age;
public void method(){
int age=10;
System.out.println(age);//10
System.out.println(this.age);//成员变量的值 0
}
}
public class StudentTest{
public static void main (String[] args){
Student s = new Student();
s.method();
}
}
StudentTest.class
main()
的类到方法区main()
入口地址)Student.class
加载 new Student()
时触发类加载private int age
的访问权限和偏移量)method()
的字节码指令集合<init>
方法(因无自定义构造方法)步骤 | 内存区域 | 具体行为 | 代码对应 |
---|---|---|---|
3 | 栈内存 | 在main方法栈帧声明局部变量s(初始值null) | Student s; |
4 | 堆内存 | 分配对象空间:对象头(12字节)+ int age(4字节)= 16字节 | new Student() |
5 | 堆内存 | 默认初始化:age=0(基本类型零值填充) | 隐式执行 |
6 | 堆内存 | 构造方法初始化:执行空参数的<init>方法(无实际操作) | 隐式调用 |
7 | 栈内存 | 将堆地址(如0x7a3f)赋值给s变量 | s = new... |
执行s.method()
时发生:
新建栈帧:在栈顶创建
method()
的独立空间,包含:
this
(指向堆地址0x7a3f
)age=10
(存储于栈帧变量表)变量访问规则:
输出语句 | 内存访问路径 | 结果 |
---|---|---|
System.out.println(age) | 访问栈帧局部变量表 | 10 |
System.out.println(this.age) | 通过this指针访问堆内存字段 | 0 |
特征 | 成员变量this.age | 局部变量age |
---|---|---|
存储位置 | 堆内存对象内部 | 栈帧局部变量表 |
生命周期 | 与对象共存亡 | 随方法栈帧销毁而消失 |
初始化值 | 默认零值(int=0) | 必须显式赋值 |
访问方式 | 需通过对象引用 | 直接访问 |
this
的底层实现当调用method()
时:
字节码层面:
java复制aload_0 // 将this引用压入操作数栈(对应堆地址0x7a3f)
getfield #2 // 根据字段偏移量读取堆中age值(#2为字段符号引用)
内存隔离机制:局部变量age
会遮蔽同名的成员变量,必须通过this.
显式穿透访问堆数据
public class Test{
public static void main (String [] args){
int a = 10;
}
}
基本数据类型:
在变量当中存储的是真实的数据值
从内存角度:数据值是存储再自己的空间中
特点:赋值给其他变量,也是赋值的真实的值。
public class TestStudent{
public static void main(String[] args){
Student s=new Student;
}
}
引用数据类型:
堆中存储的数据类型,也就是new出来的,变量中存储的是地址值。引用:就是使用其他空间中数据的意思。
从内存的角度:
数据值是存储在其他空间中,自己空间中存储的是地址值
特点:
赋值给其他变量,赋的地址值。
成员变量:类中方法外的变量
局部变量:方法中的变量
区别:
区别 | 成员变量 | 局部变量 |
---|---|---|
类中位置不同 | 类中,方法外 | 方法内、方法申明上 |
初始化值不同 | 有默认初始化值 | 没有,使用前需要完成赋值 |
内存位置不同 | 堆内存 | 栈内存 |
生命周期不同 | 随着对象的创建而存在,随着对象的消失而消失 | 随着方法的调用而存在,随着方法的运行结束而消失 |
作用域 | 整个类中有效 | 当前方法中有效 |
面向对象进阶一:静态与继承
static
表示静态,是Java中的一个修饰符,可以修饰成员方法,成员变量。
被static修饰的成员变量,叫做静态变量。
被该类的所有对象共享
不属于对象,属于类
随着类的加载而加载,优于对象存在
类名调用(推荐)
对象名调用
需求:写一个JavaBean类来描述这个班级的学生
属性:姓名、年龄、性别
行为:学习
JAVA Bean 类
package staticdemo;
public class Student {
//属性:姓名,年龄,性别
//新增:老师的姓名
private String name;
private int age;
private String gender;
public static String teacherName;
public Student() {
}
public Student(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
/**
* 获取
* @return gender
*/
public String getGender() {
return gender;
}
/**
* 设置
* @param gender
*/
public void setGender(String gender) {
this.gender = gender;
}
//行为
public void study(){
System.out.println(name+"正在学习");
}
public void show(){
System.out.println(name+","+age+","+gender+","+teacherName);
}
}
测试类
package staticdemo;
public class StudentTest {
public static void main(String[] args) {
//1.创建第一个学生对象
Student s1 = new Student();
s1.setName("张三");
s1.setAge(20);
s1.setGender("男");
//公共类,但是s2没有创建对象,所以无法访问teacherName,为null
//public String teacherName;
//于是我们想了个方法,用static修饰teacherName,但是这样就变成了静态属性,所有对象共享
//public static String teacherName;
s1.teacherName = "王老师";
//还可以用类名.属性名来访问
//Student.teacherName = "王老师";
s1.study();
s1.show();
//2.创建第二个学生对象
Student s2 = new Student();
s2.setName("李四");
s2.setAge(21);
s2.setGender("女");
s2.study();
s2.show();
}
}
main
方法首先入栈 ,在main
方法执行过程中: Student.teacherName = "阿玮老师";
,这一步只是对静态变量赋值,在栈内存中记录这个操作。Student s1 = new Student();
时,在栈内存为引用变量 s1
分配空间,存放指向堆内存中 Student
对象的地址(假设为 0x0011
) 。s1.name = "张三";
和 s1.age = 23;
,是通过 s1
引用操作堆内存中对象的实例变量。s1.show();
时,show
方法的栈帧入栈,在栈帧中记录方法内的局部变量(这里无额外局部变量)以及要操作的对象属性(通过 s1
找到堆内存对象属性)。Student s2 = new Student();
,在栈内存为引用变量 s2
分配空间,存放指向堆内存中另一个 Student
对象的地址(假设为 0x0022
) 。s2.show();
时,show
方法栈帧再次入栈,通过 s2
引用操作其对应的堆内存对象属性。new Student()
时,在堆内存创建Student对象实例。 Student
对象(对应 s1
),在堆内存中分配空间存储实例变量 name
值为 “张三” ,age
值为 23
。Student
对象(对应 s2
),在堆内存中分配空间存储实例变量 name
初始值 null
(字符串默认初始值) ,age
初始值 0
(整数默认初始值) 。teacherName
存储在堆内存的静态存储位置(静态区),值为 “阿玮老师” ,所有 Student
类的对象共享这个静态变量。注意:静态变量随类的出现而出现,优于变量。
被static修饰的成员方法,叫做静态方法。
帮助我们做一些事情的,但是不描述任何事物的类
Javabean类:用来描述一类事物的类。比如:Student、Teather、Dog等
测试类:用来检查其他类是否书写正确,带有main方法的类,是程序的入口
遵守的规范:
第一题:
需求:在实际开发中,经常会遇到一些数组使用的工具类
请按照如下要求编写一个数组的工具类:ArrayUtil
工具类:
package sta02;
public class ArrayUtil {
//私有构造方法,防止外部实例化
private ArrayUtil() {}
public static String printArray(int[] arr) {
StringBuilder sb = new StringBuilder();
sb.append( "[");
for (int i = 0; i < arr.length; i++) {
sb.append(arr[i]);
if (i < arr.length - 1) {
sb.append(", ");
}else {
sb.append("]");
}
}
return sb.toString();
}
public static double getArray(double[] arr) {
double sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i]; //累加数组元素
}
return sum/arr.length;
}
}
测试类
package sta02;
public class Testdemo {
public static void main(String[] args) {
//测试printArray方法
int[] arr = {1, 2, 3, 4, 5};
String result = ArrayUtil.printArray(arr);
System.out.println(result);
//测试getArray方法
double[] arr2 = {1.0, 2.0, 3.0, 4.0, 5.0};
double average = ArrayUtil.getArray(arr2);
System.out.println(average);
}
}
第二题:
需求:定义一个集合,用于存储3个学生对象
学生类的属性:name、age、gender
定义一个工具类,用于获取集合中最大学生的年龄
JavaBean类:
package sat03;
public class Student {
private String name;
private int age;
private String gender;
public Student() {
}
public Student(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
/**
* 获取
* @return gender
*/
public String getGender() {
return gender;
}
/**
* 设置
* @param gender
*/
public void setGender(String gender) {
this.gender = gender;
}
}
方法类:
package sat03;
import java.util.ArrayList;
public class StudentUtil {
//私有构造方法,防止外部实例化
private StudentUtil() {}
//静态方法
public static int getMaxScore(ArrayList<Student> list ) {
//1.定义一个参照物
int maxAge = list.get(0).getAge();
//2.遍历集合
for (int i = 1; i < list.size(); i++) {
if (list.get(i).getAge() > maxAge) {
maxAge = list.get(i).getAge();
}
}
return maxAge;
}
}
测试类:
package sat03;
import java.util.ArrayList;
public class testmax {
public static void main(String[] args) {
//1.创建一个集合来存储
ArrayList<Student> list = new ArrayList<Student>();
//2.创建3个学生对象
Student s1 = new Student("Zhangsan", 20, "男");
Student s2 = new Student("lisi", 23, "男");
Student s3 = new Student("wangwu", 25, "女");
//3.将学生对象添加到集合中
list.add(s1);
list.add(s2);
list.add(s3);
//4.调用方法
int max=StudentUtil.getMaxScore(list);
System.out.println(max);
}
}
只能
访问静态变量和静态方法可以
访问静态变量或者静态方法,也可以
访问非静态的成员变量和非静态的成员方法没有
this关键字的总结:
静态方法中,只能访问静态
非静态方法可以访问所有
静态方法中没有this
关键字
main
方法public class HelloWorld{
public static void main (String[] args){
System.out.println("HelloWorld");
}
}
public
: 被JVM
调用,访问权限足够大
static
:被JVM
调用,不用创建对象,直接类名访问
因为main
方法是静态的,所以测试类中其他方法也是需要是静态的
void
: 被JVM
调用,不需要给JVM返回值
main
: 一个通用的名称,虽然不是关键字,但是被JVM
识别
String[] args
:以前用于接受键盘录入数据的,现在没用
面向对象三大特征:封装、继承、多态
封装:对象代表什么,就得封装对应的数据,并提供数据对应的行为。
我们发现在Student类与Teacher类中有重复的元素,于是为了使程序更加便捷便出现了”继承“
java
中提供一个关键字extends
,用这个关键字,我们可以让一个类和另一个类建立起继承关系
public class Student extends Person{}
子类(派生类)
,Person称为父类(基类或超类)
优点:
什么时候用继承?
当类与类之间,存在相同(共性)的内容,并满足子类是父类中的一种,就可以考虑使用继承,来优化代码
java中只支持单继承
,不支持多继承
,但支持多层继承
单继承:一个子类只能继承一个直接父类
不支持多继承:子类不能同时继承多个父类
多层继承:子类A继承父类B,父类B可以继承父类C
C是A的间接父类
每一个类都直接或者间接的继承于Object
练习:
核心点:共性内容抽取,子类是父类中的一种
写代码是从父类开始写,最后写子类
JAVABean类
package st5;
public class Animal {
public void eat() {
System.out.println("我会吃饭");
}
public void water (){
System.out.println("我会喝水");
}
}
package st5;
public class Cat extends Animal {
public void mice() {
System.out.println("我会抓老鼠");
}
}
package st5;
public class Dog extends Animal {
public void lookhome() {
System.out.println("我会看家");
}
}
package st5;
public class Ragdoll extends Cat{
}
package st5;
public class Lihua extends Cat{
}
package st5;
public class Husky extends Dog{
public void breakhome() {
System.out.println("我会拆家");
}
}
package st5;
public class Teddy extends Dog{
public void Ceng(){
System.out.println("我喜欢蹭一蹭");
}
}
测试类:
package st5;
public class Test {
public static void main(String[] args) {
//创建对象并调用方法
//创建布偶猫的对象
Ragdoll rd = new Ragdoll();
System.out.println("我是布偶猫");
rd.mice();
rd.water();
rd.eat();
System.out.println("-------------------");
//创建狸花猫的对象
Lihua lh = new Lihua();
System.out.println("我是狸花猫");
lh.mice();
lh.water();
lh.eat();
System.out.println("-------------------");
//创建泰迪的对象
Teddy td = new Teddy();
System.out.println("我是泰迪");
td.lookhome();
td.water();
td.eat();
td.Ceng();
System.out.println("-------------------");
//创建哈士奇的对象
Husky hs = new Husky();
System.out.println("我是哈士奇");
hs.lookhome();
hs.water();
hs.eat();
hs.breakhome();
}
}
试运行:
注意:子类只能访问父类中非私有的成员
构造方法 | 非私有 不能 | private 不能 |
---|---|---|
成员变量 | 非私有 能 | private 能 但不能直接用 |
成员方法 | 虚方法表 能 | 否则 不能 |
虚方法表:就是经常要用的方法,什么叫虚方法表呢?非private
非static
非final
public class Fu{
String name = "Fu";
}
public class Zi extends Fu{
String name = "Zi";
public void ziShow(){
String name = "ziShow";
System.out.println(name);
}
}
//就近原则:谁离我近,我就用谁
//完整版就近原则:先在局部位置找,本类成员位置找,父类成员位置找,逐级往上
//run:ziShow
如果出现了重名的成员变量怎么找:
System.out.println(name);//从局部位置开始往上找
System.out.println(this.name);//从本类成员位置开始往上找
System.out.println(super.name);//从父类成员位置开始往上找
public class Test{
public static void main (String [] args){
Zi z = new Zi();
z.ziShow();
}
}
public class Fu{
String name = "Fu";
}
public class Zi extends Fu{
String name = "Zi";
public void ziShow(){
String name = "ziShow";
System.out.println(name);//ziShow
System.out.println(this.name);//Zi
System.out.println(super.name);//Fu
}
}
直接调用满足就近原则:谁离我近,我就用谁
super调用,直接访问父类
package jicehng;
public class test {
public static void main(String[] args) {
//创建一个对象
Student s = new Student();
s.lunch();
/*
吃面条
咖啡
吃米饭
喝水
*/
}
}
class Person {
public void eat(){
System.out.println("吃米饭");
}
public void water(){
System.out.println("喝水");
}
}
class Student extends Person {
public void lunch(){
this.eat();//就近读取子类吃面条
this.water();//就近读取子类咖啡
super.eat();//调用父类吃米饭
super.water();//调用父类喝水
}
public void eat(){
System.out.println("吃面条");
}
public void water(){
System.out.println("咖啡");
}
}
方法的重写:
当父类的方法不能满足子类现在的需求时,需要进行方法重写
书写格式:
在继承体系中 ,子类出现了和父类中一模一样的方法声明,我们就称子类这个方法是重写的方法
@Override重写体系
方法重写注意事项和要求:
JAVABean类:
package jice;
public class Dog {
public void eat() {
System.out.println("Dog is eating.");
}
public void drink() {
System.out.println("Dog is drinking.");
}
public void lookhome() {
System.out.println("Dog is lookhome.");
}
}
package jice;
public class hashiqi extends Dog {
public void breakhome() {
System.out.println("hashiqi is breakhome.");
}
}
package jice;
public class shapi extends Dog{
@Override
public void eat() {
super.eat();// 调用父类的eat方法
System.out.println("shapi is eating gouliang.");
}
}
package jice;
public class chinesedog extends Dog{
@Override
public void eat() {
super.eat();// 调用父类的eat方法
System.out.println("Chinesedog is eating chinesefood.");
}
}
测试类
package jice;
public class test {
public static void main(String[] args) {
hashiqi hashiqi = new hashiqi();
hashiqi.eat();
hashiqi.drink();
hashiqi.lookhome();
shapi shapi = new shapi();
shapi.eat();
shapi.drink();
shapi.lookhome();
chinesedog chinesedog = new chinesedog();
chinesedog.eat();
chinesedog.drink();
}
}
怎么调用父类构造方法的?
super()
,不写也存在,且必须在第一行super
进行调用例如代码实现:
package sta08;
public class Person {
String name;
int age;
public Person() {
System.out.println("父类的无参构造方法");
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
package sta08;
public class Student extends Person {
public Student() {
//子类构造方法中隐藏的super()去访问父类的无参构造
super();
System.out.println("子类的无参构造方法");
}
}
package sta08;
public class test {
public static void main(String[] args) {
//创建学生对象
Student stu = new Student();
}
}
this:理解为一个变量,表示当前发给发调用者的地址值;
super:代表父类存储空间
关键字 | 访问成员变量 | 访问成员方法 | 访问构造方法 |
---|---|---|---|
this | this.成员变量 访问本类成员变量 | this.成员方法(…) 访问本类成员方法 | this(…) 访问本类构造方法 |
super | super.成员变量 访问父类成员变量 | super.成员方法(…) 访问父类成员方法 | super(…) 访问父类构造方法 |
package sta09;
public class Employee {
//1.类名见名知意
//2.属性私有
//3.构造方法(空参 带全部参数的构造)
//4.get/set
private String id;
private String name;
private double salary;
public Employee() {
}
public Employee(String id, String name, double salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
//方法
public void work() {
System.out.println("员工在工作");
}
public void eat(){
System.out.println("员工在吃饭吃米饭");
}
}
package sta09;
public class Manager extends Employee{
private double bonus;
public Manager() {
}
//带全部参数的构造
public Manager(String id, String name, double salary,double bonus) {
super(id, name, salary);
this.bonus = bonus;
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
//重写父类的方法
@Override
public void work() {
System.out.println("经理在管理其他人");
}
}
package sta09;
public class Cook extends Employee{
public Cook() {
}
public Cook(String id, String name, double salary) {
super(id, name, salary);
}
@Override
public void work() {
System.out.println("厨师在烹饪");
}
}
package sta09;
public class Test {
public static void main(String[] args) {
//创建一个经理对象
Manager manager = new Manager("001","小明",100000,50000);
System.out.println(manager.getId()+","+manager.getName()+
","+manager.getSalary()+","+manager.getBonus());
manager.work();
manager.eat();
//创建一个厨师对象
Cook cook = new Cook("002","小红",80000);
System.out.println(cook.getId()+","+cook.getName()+
","+cook.getSalary());
cook.work();
cook.eat();
}
}
面向对象三大特征之一:封装、继承、多态
有了封装才有了面向对象,有了面向对象才有了继承与多态
封装:对象代表什么,就得封装对应的数据,并提供数据对应的行为
随着业务的增加,这类对象越来越多,对象中重复的行为越来越多,于是我们把重复的内容提取出来,就有了子类和父类,这就是继承。我们用继承解决JavaBean中代码重复的问题,这也就是多态的前提条件。没有继承就没有多态。
同类型的对象,表现出的不同形态
父类类型 对象名称= 子类对象;
父类
package sta01;
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public void show() {
System.out.println(name + " " + age);
}
}
子类
package sta01;
public class Student extends Person {
@Override
public void show() {
System.out.println("学生信息为:"+getName()+" "+getAge());
}
}
package sta01;
public class Teacher extends Person {
@Override
public void show() {
System.out.println("教师信息为:"+getName()+" "+getAge());
}
}
package sta01;
public class Administrator extends Person {
@Override
public void show() {
System.out.println("管理员信息为:"+getName()+" "+getAge());
}
}
测试类
package sta01;
public class test {
public static void main(String[] args) {
//创建三个学生对象
Student s1 = new Student();
s1.setName("小明");
s1.setAge(23);
Student s2 = new Student();
s2.setName("小红");
s2.setAge(23);
Student s3 = new Student();
s3.setName("小刚");
s3.setAge(23);
//创建一个老师对象
Teacher t1 = new Teacher();
t1.setName("王老师");
t1.setAge(45);
//创建一个管理员对象
Administrator a1 = new Administrator();
a1.setName("李管理员");
a1.setAge(23);
//传递方法
register(s1);
register(s2);
register(s3);
register(t1);
register(a1);
}
//这个方法能接受老师,也能接受学生,还能接受管理员
public static void register(Person p) {
p.show();
}
}
使用父类型作为参数,可以接受所有子类对象,体现了多态的扩展性与便利
变量调用:编译看左边,运行也看左边
方法调用:编译看左边,运行看右边
package sta02;
public class test {
public static void main(String[] args) {
//创建对象(多态方式)
Animal animal = new Dog();
//成员变量
//编译看左边,运行看左边
//编译看左边:java编译器在编译代码时,会检查左边的父类中有没有这个变量,如果有就通过编译,否则就会报错。
//运行看左边:在运行时,实际上是在执行父类中的变量,因为子类中没有这个变量,所以会输出父类中的变量值。
System.out.println(animal.name);//动物
//成员方法
//编译看左边,运行看右边
//编译看左边:java编译器在编译代码时,会检查左边的父类中有没有这个方法,如果有就通过编译,否则就会报错。
//运行看右边:在运行时,实际上是在执行子类中的方法,因为子类中重写了这个方法,所以会输出子类中的方法值。
animal.show();//Dog-----show方法
//理解
//Animal animal = new Dog();
//现在用animal类型的,所以默认都会从父类Animal中找
//成员变量:在子类的对象中,从父类的成员位置查找,无论是否有重写,都只看左边(父类)
//成员方法:如果子类对方法进行了重写,那么在虚方法表中是会把父类的方法覆盖掉,只保留子类的方法,
//所以在运行时,调用的是子类的方法,所以看右边(子类)
}
}
class Animal {
String name="动物";
public void show(){
System.out.println("Animal-----show方法");
}
}
class Dog extends Animal{
String name="狗";
@Override
public void show(){
System.out.println("Dog-----show方法");
}
}
程序内存图解说明:
1.类加载阶段(方法区准备)
程序运行前,Animal.class
、Dog.class
、Test.class
会被类加载器加载到 方法区:
Animal
虚方法表存 show()
;Dog
虚方法表因重写,show()
指向子类实现。2.main
方法进栈(栈内存开始执行)
Test
类的 main
方法作为程序入口,进入栈内存:
Animal a
)、操作数栈、方法出口等。 Animal a = new Dog()
; 001
),创建 Dog
对象:包含父类 Animal
继承的成员(name = "动物"
)和自身成员(name = "狗"
)。a
保存堆对象的引用(地址 001
)。3. 执行 sout(a.name)
(成员变量调用)
a
的声明类型是 Animal
,所以找 Animal
类的 name
。a
关联的 Animal
成员变量(堆里 Animal
部分的 name = "动物"
),输出 动物
。4. 执行 a.show()
(成员方法调用)
a
声明类型 Animal
,确认 Animal
有 show()
方法,编译通过。a
实际引用的Dog
对象,去Dog
的虚方法表找 show()
实现。 Dog
的 show()
方法 进栈,执行 System.out.println("Dog --- show方法")
,输出 Dog --- show方法
。show()
栈帧 出栈。5. 程序结束,栈帧出栈
main
方法执行完毕,其栈帧从栈内存弹出,程序结束。堆中对象后续由垃圾回收器(GC)根据可达性分析回收(若无引用关联)。
Person p = new Student () ;
p.work() ; //业务逻辑发生改变时,后续代码无需修改
//假如,我想将Student改为Teacher,只需要把new Student () 改为new Teacher () 就可以了
多态的弊端:
不能使用子类特有的功能,如果要强行使用子类的特有功能,要进行强行转换。
引用数据类型的类型转换,有几种方式?
自动类型转换、强制类型转换
Person p = new Student () ;//自动类型转换
Student s = (Student)p;//强制类型转换
强制类型转换能解决什么问题?
instanceof
关键字进行判断// 定义父类:动物
abstract class Animal {
public void eat() {
System.out.println("动物在进食");
}
// 抽象方法,由子类实现
public abstract void makeSound();
}
// 子类:狗(有特有方法guardHouse)
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("狗在汪汪叫");
}
// 狗特有的方法:看家
public void guardHouse() {
System.out.println("狗在看家护院");
}
}
// 子类:猫
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("猫在喵喵叫");
}
}
// 测试类
public class PolymorphismLimitationDemo {
public static void main(String[] args) {
// 多态:父类引用指向子类对象
Animal dog = new Dog();
Animal cat = new Cat();
// 调用父类方法(正常工作)
dog.eat();
dog.makeSound();
// 尝试调用子类特有方法(编译错误!)
// dog.guardHouse();
// 错误: 找不到符号
// 符号: 方法 guardHouse()
// 位置: 类型为Animal的变量dog
}
}
通过强制转换解决弊端
public class PolymorphismCastingDemo {
public static void main(String[] args) {
// 多态引用
Animal dog = new Dog();
// 1. 安全的强制类型转换
if (dog instanceof Dog) {
Dog realDog = (Dog) dog; // 强制类型转换
realDog.guardHouse(); // 访问子类特有方法
}
//instanceof的用法
//他返回的是boolean值,左侧是要检查的对象引用,右侧是类名、接口名或是数组类型
// 2. 不安全的强制类型转换(会抛出异常)
// Cat cat = (Cat) dog; // 运行时抛出ClassCastException
// cat.makeSound();
//所以使用instanceof验证
// 3. 类型转换最佳实践
useDogFeatures(dog);
}
// 最佳实践:封装类型转换逻辑
public static void useDogFeatures(Animal animal) {
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.guardHouse();
dog.makeSound();
} else {
System.out.println("这不是一只狗,无法执行看家功能");
}
}
}
练习:
JavaBean类
package sta03;
public class Animal {
private int age;
private String color;
public Animal() {
}
public Animal(int age, String color) {
this.age = age;
this.color = color;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
/**
* 获取
* @return color
*/
public String getColor() {
return color;
}
/**
* 设置
* @param color
*/
public void setColor(String color) {
this.color = color;
}
public void eat(String something) {
System.out.println("动物在吃:"+something);
}
}
package sta03;
public class Dog extends Animal{
//空参构造
//带上全部参数的构造
public Dog() {
}
public Dog(int age, String color) {
super(age, color);
}
//行为
//eat(String something)(something是吃的东西)
//看家lookHome()
@Override
public void eat(String something) {
System.out.println(getAge()+"岁的"+getColor()+"颜色的狗两只前腿死死抱住"+something+"猛吃");
}
public void lookHome(){
System.out.println("狗在看家");
}
}
package sta03;
public class Cat extends Animal {
public Cat() {
}
public Cat (int age, String color) {
super(age, color);
}
@Override
public void eat(String something) {
System.out.println(getAge()+"岁的"+getColor()+"颜色的猫眯着眼睛吃"+something);
}
public void catchMouse(){
System.out.println("猫在抓老鼠");
}
}
package sta03;
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
//饲养狗
public void keepPet(Dog dog, String something) {
System.out.println("年龄为"+age+"岁的"+name+"在养一只"+dog.getColor()+"颜色的狗"+dog.getAge()+"岁的狗");
dog.eat(something);
}
//饲养猫
public void keepPet(Cat cat, String something) {
System.out.println("年龄为"+age+"岁的"+name+"在养一只"+cat.getColor()+"颜色的猫"+cat.getAge()+"岁的猫");
cat.eat(something);
}
}
测试类
package sta03;
public class test {
public static void main(String[] args) {
//创建对象并调用方法
Dog dog = new Dog(2, "黑色");
Person person1 = new Person("小明", 18);
person1.keepPet(dog, "骨头");
Person person2 = new Person("小红", 20);
Cat cat = new Cat(3, "白色");
person2.keepPet(cat, "鱼");
}
}
包就是文件夹。用来管理各种不同功能的Java类,方便后期代码维护
公司域名反写+包的作用,需要全部英文小写,见名知意。比如:com.ensichen.domain
package com.ensichen.domain;
public class Student {
私有化成员变量
构造方法
成员方法
}
包名+类名=全类名 com.ensichen.domain.Student
或者叫全限定名
使用其他类时要使用全类名
public class test {
public static void main(String [] args ){
com.ensichen.domain.Student s = new com.ensichen.domain.Student();
}
}
上面的写法太复杂于是就有了导包
import com.ensichen.domain.Student;//导包
public static void main(String [] args){
Student s = new Student () ;
}
使用同一个包中的类时,不需要导包
public class test {
public static void main(String[] args) {
//1.使用同一个包中的类时,不需要导包
//创建对象
Student s = new Student();
s.setName("张三");
s.setAge(20);
System.out.println(s.getName()+":"+s.getAge());
}
}
使用java.lang
包中的类时(JAVA核心包),不需要导包
public class test {
public static void main(String[] args) {
//2.使用java.lang包下的类时,不需要导包
String a = "abc";
System.out.println(a);
//我们要找String字符串到底定义在哪个包中,我们可以按住Ctrl键点击String,
// 或者选中String,然后按Ctrl键+鼠标左键,或者Ctrl+B就可以跳转到String的源码中
}
}
其他情况都需要导包
如果同时使用两个包中的同名类,需要用全类名(用到的概率很小)
final
最终的——不可被改变的
final可以修饰方法、类、变量
表明该方法是最终方法,不能被重写
在父类的方法中我们用了final
修饰,可我们会发现,子类中的重写方法会报错
表明该类是最终类,不能被继承
我们用final
修饰父类,我们就会发现子类报错
那我们的使用场景是什么呢?
当我们需要定义规则,不会被改变的时候就需要用到final 修饰类。Object类当中就有这样设计的。Ctrl+N 搜索Object我们就会看到final
修饰的类
这里我们会发现没有方法体,实际上这里是用native
,表示当前的方法体是调用本地汇编语言写的方法体。
叫做常量,只能被赋值一次
我们二次对a
赋值时就会报错
这在一些类中也有应用,比如Math
类中的pi
是final
修饰的固定值。
插播:行文至此,说到pi
这与博主的网名有很大关系。博主的生日约等于pi
的值,同一天也是爱因斯坦的生日。同理可得:
博主约等于爱因斯坦
,并且博主真实姓名最后一个字叫做晨
,故博主网名叫做爱因斯晨
。是不是很有道理呢?
实际开发中,常量一般作为系统的配置信息,方便维护,提高可读性
常量的命名规范:
final
修饰的变量是基本类型
:那么变量存储的数据值
不能发生改变。
final
修饰的变量是引用类型
:那么变量存储的地址值
不能发生改变,对象内部可变。
核心:常量记录的数据是不能发生变化的
我们之前讲过,字符串里的内容是不能发生改变的,原因就是因为final
private
是私有的,外界不能获取。于是字符串靠pravite
和final
使其不可修改.
用来控制一个成员能够被访问的范围的
可以修饰成员变量,方法,构造方法,内部类
有四种作用范围由小到大(private
<空着不写
<protected
<public
)
修饰符 | 同一个类中 | 同一个包中其他类 | 不同包下的子类 | 不同包下的无关类 |
---|---|---|---|---|
private | √ | |||
空着不写 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
private 私房钱,只能自己用
默认:只能本包用
protected 受保护的
public 公共的
实际开发中,一般只用private和public
成员变量是私有的
方法是公开的
特例:如果方法中的代码是抽取其他方法中共性代码,这个方法一般也私有
局部代码块
局部位置:方法里面的代码块。局部变量的作用范围,为了节约内存(提前结束变量的生命周期,已淘汰)
构造代码块
public class stu {
private String name;
private int age;
public stu() {
System.out.println("开始创建对象了");
}
public stu(String name, int age) {
this.name = name;
this.age = age;
System.out.println("开始创建对象了");
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "stu{name = " + name + ", age = " + age + "}";
}
}
在上述代码中我们看到了重复的代码,于是我们把它抽取放到局部代码块中,如下
public class stu {
private String name;
private int age;
{
System.out.println("开始创建对象了");
}
public stu() {
}
public stu(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "stu{name = " + name + ", age = " + age + "}";
}
}
构造代码块:写在成员位置的代码块
作用:可以把多个构造方法中重复的代码抽取出来
执行时机:我们在创建本类对象的时候会先执行构造代码块再执行
我们可以验证一下:
public class stu {
private String name;
private int age;
{
System.out.println("开始创建对象了");
}
public stu() {
System.out.println("空参构造器");
}
public stu(String name, int age) {
this.name = name;
this.age = age;
System.out.println("有参构造器");
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "stu{name = " + name + ", age = " + age + "}";
}
}
public class teest {
public static void main(String[] args) {
stu s1 = new stu();
stu s2 = new stu("zhnagsan", 20);
}
}
我们可以看到构造代码块优先执行于构造方法,但是这种方法不是很灵活,当我不想重复执行时就没有办法,于是,通常有了
重复代码我们都是:(不够灵活)
静态代码块
格式:static{}
特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发,只执行一次
使用场景:在类加载的时候,做一些数据初始化的时候使用
public class stu {
private String name;
private int age;
static {
System.out.println("静态代码块执行了");
}
public stu() {
System.out.println("空参构造器");
}
public stu(String name, int age) {
this.name = name;
this.age = age;
System.out.println("有参构造器");
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "stu{name = " + name + ", age = " + age + "}";
}
}
public class teest {
public static void main(String[] args) {
stu s1 = new stu();
stu s2 = new stu("zhnagsan", 20);
}
}
执行时机:随着类的加载而加载,并且只执行一次,用于数据的初始化
当javabean的内容越来越多问题就出现了,于是就有了继承,将重复的属性提取出来
但是对象的一些行为不一样,方法体不一样,于是就有了抽象类
将共性的行为方法抽取到父类之后
由于每一个子类执行的内容不一样,所以,在父类中不能确定具体的方法体。该方法就可以定义为抽象方法
如果一个类中存在抽象方法,那么该类就必须声明为抽象类
抽象类不能实例化
package demo01;
public abstract class Person {
public abstract void sayHello();
}
package demo01;
public class test {
public static void main(String[] args) {
// 抽象类不能被实例化,只能被继承
Person person = new Person();
}
}
抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
package demo01;
public abstract class Person {
//抽象类不一定有抽象方法
public void sayHello(){
System.out.println("Hello");
}
}
package demo01;
public abstract class Person {
//有抽象方法一定是抽象类
public abstract void sayHello();
}
可以有构造方法
抽象类的子类
要么重写抽象类中所有抽象方法
要么是抽象类
package sta02;
public abstract class Animal {
private String name;
private int age;
public Animal() {
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public void drink(){
System.out.println("动物喝水");
}
public abstract void eat();
}
package sta02;
public class Frog extends Animal{
public Frog() {
}
public Frog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("青蛙在吃虫子");
}
}
package sta02;
public class Dog extends Animal{
public Dog() {
}
public Dog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("狗在吃骨头");
}
}
package sta02;
public class Sheep extends Animal {
public Sheep() {
}
public Sheep(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("羊在吃草");
}
}
package sta02;
public class test {
public static void main(String[] args) {
Frog frog = new Frog("小绿", 1);
System.out.println(frog.getName() + " " + frog.getAge());
frog.eat();
frog.drink();
Dog dog = new Dog("小蓝", 2);
System.out.println(dog.getName() + " " + dog.getAge());
dog.eat();
dog.drink();
Sheep sheep = new Sheep("小红", 3);
System.out.println(sheep.getName() + " " + sheep.getAge());
sheep.eat();
sheep.drink();
}
}
于是抽象方法让程序更好维护,更兼容
为什么会有接口?
用来定义行为规范
接口与抽象类的异同
接口定义行为契约(能做什么),支持多重实现,适合解耦与组合;抽象类定义类型抽象(是什么),支持单继承,适合状态共享与模板复用。
interface
来定义
public interface 接口名{}
implements
关键字表示
public class 类名 implements
接口名{}
注意一
:接口和类的实现关系,可以单实现,也可以多实现。
public class 类名 implements
接口名1,接口名2{}
注意二
:实现类还可以在继承一个类的同时实现多个接口
public class 类名 extends 父类 implements
接口名1,接口名2{}
练习:
无接口写法:
package demo01;
public abstract class Animal {
private String name;
private int age;
public Animal() {
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public abstract void eat() ;
}
package demo01;
public class frog extends Animal{
public frog() {
}
public frog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("青蛙在吃虫子");
}
public void swimming() {
System.out.println("青蛙在游泳");
}
}
package demo01;
public class dog extends Animal{
public dog() {
}
public dog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("狗在吃骨头");
}
public void dig() {
System.out.println("狗在刨地");
}
}
package demo01;
public class rabbit extends Animal{
public rabbit() {
}
public rabbit(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("兔子在吃胡萝卜");
}
}
package demo01;
public class test {
public static void main(String[] args) {
dog dog = new dog("小黑", 2);
System.out.println(dog.getName() + "," + dog.getAge());
dog.eat();
dog.dig();
frog f = new frog("小绿", 1);
System.out.println(f.getName() + "," + f.getAge());
f.eat();
f.swimming();
rabbit r = new rabbit("小红", 3);
System.out.println(r.getName() + "," + r.getAge());
r.eat();
}
}
加入接口的代码:
当我们新建接口时,应选择接口而不是类,新建之后,我们可以看出来,类与接口的图标表示不一样
详细代码:
package demo02;
public abstract class Animal {
private String name;
private int age;
public Animal() {
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public abstract void eat() ;
}
package demo02;
public interface swim {
public abstract void swimming();
}
package demo02;
public class Dog extends Animal implements swim{
public Dog() {
}
public Dog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("狗吃骨头");
}
@Override
public void swimming() {
System.out.println("狗会游泳");
}
}
package demo02;
public class Frog extends Animal implements swim{
public Frog() {
}
public Frog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("青蛙吃虫子");
}
@Override
public void swimming() {
System.out.println("青蛙会游泳");
}
}
package demo02;
public class Rabbit extends Animal {
public Rabbit() {
}
public Rabbit(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("兔子吃胡萝卜");
}
}
package demo02;
public class test {
public static void main(String[] args) {
Dog dog = new Dog("小黑", 2);
System.out.println(dog.getName()+" "+dog.getAge());
dog.eat();
dog.swimming();
Frog f = new Frog("小绿", 1);
System.out.println(f.getName()+" "+f.getAge());
f.eat();
f.swimming();
Rabbit r = new Rabbit("小紫", 3);
System.out.println(r.getName()+" "+r.getAge());
r.eat();
}
}
public static final
public abstract
练习:
// 说英语的接口
public interface SpeakEnglish {
void speakEnglish();
}
// 抽象类 - 人
public abstract class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 获取姓名
public String getName() {
return name;
}
// 设置姓名
public void setName(String name) {
this.name = name;
}
// 获取年龄
public int getAge() {
return age;
}
// 设置年龄
public void setAge(int age) {
this.age = age;
}
// 抽象方法,由子类实现具体行为
public abstract void doActivity();
}
// 抽象类 - 运动员,继承 Person
public abstract class Athlete extends Person {
public Athlete(String name, int age) {
super(name, age);
}
// 抽象方法,运动员的 “学习打球” 行为,子类具体实现
public abstract void learnPlayBall();
}
// 抽象类 - 教练,继承 Person
public abstract class Coach extends Person {
public Coach(String name, int age) {
super(name, age);
}
// 抽象方法,教练的 “教打球” 行为,子类具体实现
public abstract void teachPlayBall();
}
// 乒乓球运动员,继承 Athlete 并实现 SpeakEnglish 接口
public class PingPongAthlete extends Athlete implements SpeakEnglish {
public PingPongAthlete(String name, int age) {
super(name, age);
}
// 实现运动员 “学习打球” 抽象方法
@Override
public void learnPlayBall() {
System.out.println(getName() + "(乒乓球运动员,年龄" + getAge() + ")正在学习打乒乓球");
}
// 实现说英语接口方法
@Override
public void speakEnglish() {
System.out.println(getName() + "(乒乓球运动员)正在说英语交流");
}
// 实现 Person 类的抽象方法
@Override
public void doActivity() {
learnPlayBall();
}
}
// 篮球运动员,继承 Athlete
public class BasketballAthlete extends Athlete {
public BasketballAthlete(String name, int age) {
super(name, age);
}
// 实现运动员 “学习打球” 抽象方法
@Override
public void learnPlayBall() {
System.out.println(getName() + "(篮球运动员,年龄" + getAge() + ")正在学习打篮球");
}
// 实现 Person 类的抽象方法
@Override
public void doActivity() {
learnPlayBall();
}
}
// 乒乓球教练,继承 Coach 并实现 SpeakEnglish 接口
public class PingPongCoach extends Coach implements SpeakEnglish {
public PingPongCoach(String name, int age) {
super(name, age);
}
// 实现教练 “教打球” 抽象方法
@Override
public void teachPlayBall() {
System.out.println(getName() + "(乒乓球教练,年龄" + getAge() + ")正在教打乒乓球");
}
// 实现说英语接口方法
@Override
public void speakEnglish() {
System.out.println(getName() + "(乒乓球教练)正在说英语交流");
}
// 实现 Person 类的抽象方法
@Override
public void doActivity() {
teachPlayBall();
}
}
// 篮球教练,继承 Coach
public class BasketballCoach extends Coach {
public BasketballCoach(String name, int age) {
super(name, age);
}
// 实现教练 “教打球” 抽象方法
@Override
public void teachPlayBall() {
System.out.println(getName() + "(篮球教练,年龄" + getAge() + ")正在教打篮球");
}
// 实现 Person 类的抽象方法
@Override
public void doActivity() {
teachPlayBall();
}
}
// 测试类,验证各个类功能
public class Test {
public static void main(String[] args) {
// 测试乒乓球运动员
PingPongAthlete pingPongAthlete = new PingPongAthlete("小张", 20);
pingPongAthlete.doActivity();
pingPongAthlete.speakEnglish();
// 测试篮球运动员
BasketballAthlete basketballAthlete = new BasketballAthlete("小李", 22);
basketballAthlete.doActivity();
// 测试乒乓球教练
PingPongCoach pingPongCoach = new PingPongCoach("王教练", 40);
pingPongCoach.doActivity();
pingPongCoach.speakEnglish();
// 测试篮球教练
BasketballCoach basketballCoach = new BasketballCoach("赵教练", 45);
basketballCoach.doActivity();
}
}
JDK7以前:接口中只能定义抽象方法
JDK8的新特性:接口中可以定义有方法体的方法(默认、静态)
允许在接口中定义默认方法,需要使用关键字default修饰
接口中默认方法的定义格式:
public default void show ( ) { }
接口中默认方法的注意事项:
默认方法不是抽象方法,所以不强制被重写。但如果被重写,重写的时候去掉default关键字
public 可以省略,default不能省略
如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写
// 接口定义
interface DefaultMethodInterface {
// 默认方法(带方法体)
default void defaultMethod() {
System.out.println("这是接口中的默认方法");
}
// 抽象方法(需实现类实现)
void abstractMethod();
}
// 实现类
class DefaultMethodImpl implements DefaultMethodInterface {
@Override
public void abstractMethod() {
System.out.println("实现抽象方法");
}
// 可选:重写默认方法
@Override
public void defaultMethod() {
System.out.println("重写默认方法");
}
}
// 测试类
public class DefaultMethodDemo {
public static void main(String[] args) {
DefaultMethodImpl obj = new DefaultMethodImpl();
obj.defaultMethod(); // 调用默认方法
obj.abstractMethod(); // 调用抽象方法
}
}
允许在接口中定义定义静态方法,需要用static修饰
接口中静态方法的格式:
格式:public static 返回值类型 方法名 (参数列表){ }
范例:public static void show (){}
// 接口定义
interface StaticMethodInterface {
// 静态方法(带方法体)
static void staticMethod() {
System.out.println("这是接口中的静态方法");
}
// 抽象方法
void abstractMethod();
}
// 实现类
class StaticMethodImpl implements StaticMethodInterface {
@Override
public void abstractMethod() {
System.out.println("实现抽象方法");
}
}
// 测试类
public class StaticMethodDemo {
public static void main(String[] args) {
StaticMethodImpl obj = new StaticMethodImpl();
obj.abstractMethod(); // 调用抽象方法
StaticMethodInterface.staticMethod(); // 只能通过接口名调用静态方法
}
}
接口中静态方法的注意事项:
JDK9的新特性:接口中可以定义私有方法
接口中私有方法的定义格式:
格式1:private 返回值类型 方法名 (参数列表){ }
范例1:private void show ( ) { }
// JDK9+ 接口(需 Java 9 及以上版本)
interface PrivateInstanceMethodInterface {
// 默认方法(调用私有实例方法)
default void defaultMethod() {
System.out.println("默认方法逻辑");
privateMethod(); // 调用私有实例方法
}
// 私有实例方法(JDK9 特性)
private void privateMethod() {
System.out.println("这是接口中的私有实例方法");
}
}
// 实现类
class PrivateInstanceMethodImpl implements PrivateInstanceMethodInterface {
// 无需实现私有方法,它们对实现类不可见
}
// 测试类
public class PrivateInstanceMethodDemo {
public static void main(String[] args) {
PrivateInstanceMethodImpl obj = new PrivateInstanceMethodImpl();
obj.defaultMethod(); // 调用默认方法(间接调用私有方法)
}
}
格式2:private static 返回值类型 方法名 (参数列表){ }
范例2:private static void method ( ) { }
// JDK9+ 接口
interface PrivateStaticMethodInterface {
// 默认方法(调用私有静态方法)
default void defaultMethod() {
System.out.println("默认方法逻辑");
privateStaticMethod(); // 调用私有静态方法
}
// 静态方法(调用私有静态方法)
static void staticMethod() {
System.out.println("静态方法逻辑");
privateStaticMethod(); // 静态方法只能调用静态私有方法
}
// 私有静态方法(JDK9 特性)
private static void privateStaticMethod() {
System.out.println("这是接口中的私有静态方法");
}
}
// 实现类
class PrivateStaticMethodImpl implements PrivateStaticMethodInterface {
// 无需实现私有方法
}
// 测试类
public class PrivateStaticMethodDemo {
public static void main(String[] args) {
PrivateStaticMethodImpl obj = new PrivateStaticMethodImpl();
obj.defaultMethod(); // 调用默认方法
PrivateStaticMethodInterface.staticMethod(); // 调用静态方法
}
}
总结:
XXXAdapter
,实现对应的接口;对接口中的抽象方法进行空实现;让真正的实现类继承中间类,并重写需要用的方法;为了避免其他类创建适配器类的对象,中间的适配器类用abstract进行修饰类的五大成员之一:属性、方法、构造方法、代码块、内部类
什么是内部类?
在一个类的里面,在定义一个类。
举例:在A类的内部定义B类,B类就要称为内部类
为什么要学习内部类?
需求:写一个Javabean类描述汽车
属性:汽车的品牌,车龄,颜色,发动机的品牌,使用年限
public class Car{
String carName;
int carAge;
int carColor;
String engineName;
int engineAge;
}
public class Car{//外部类
String carName;
int carAge;
int carColor;
clss Engine{//内部类
String engineName;
int engineAge;
}
}
表示的事物是外部类的一部分
内部类单独出现没有任何意义
package demo03;
public class Car {
String name;
String color;
int age;
public void show() {
System.out.println(this.name);
Engine engine = new Engine();
System.out.println(engine.engineType);
}
class Engine{
String engineType;
int engineYears;
public void show(){
System.out.println("发动机型号:"+engineType);//可以访问外部类的属性
System.out.println("车的名称"+name);//可以访问外部类的私有属性,加上private后,内部类也可以访问
}
}
}
package demo03;
public class test {
public static void main(String[] args) {
Car car = new Car();
car.name = "奔驰";
car.color = "黑色";
car.age = 1;
car.show();
}
}
写在成员位置的,属于外部类的成员
public class Car{//外部类
String carName;
int carAge;
int carColor;
clss Engine{//成员内部类
String engineName;
int engineAge;
}
}
成员内部类可以被一些修饰符所修饰,比如:private, 默认,protected,public,static 等
在成员内部类里面,JDK16之前不能定义静态变量,JDK16开始才可以定义静态变量。
获取成员内部类对象:
方法一:在外部类中编写方法,对外提供内部类的对象
方法二:直接创建格式:外部类名.内部类名 对象名=外部类对象. 内部类对象;
public class BodyAndOrgans {
public static void main(String[] args) {
// 创建一个人的身体
Body person = new Body("张三");
// 通过身体获取心脏(方式一)
Body.Heart heart = person.getHeart();
heart.pumpBlood();
// 直接创建肺(方式二)
Body.Lung leftLung = person.new Lung("左肺");
leftLung.breathe();
// 身体运动,间接调用内部类方法
person.exercise();
}
}
class Body {
private String name;
private int oxygenLevel = 100; // 氧气水平 - 私有变量
public Body(String name) {
this.name = name;
System.out.println(name + "的身体已创建");
}
// 成员内部类:心脏
public class Heart {
public void pumpBlood() {
System.out.println(name + "的心脏正在输送血液,使用氧气水平:" + oxygenLevel);
// 直接访问外部类的私有变量
oxygenLevel -= 10;
}
}
// 成员内部类:肺
public class Lung {
private String position;
public Lung(String position) {
this.position = position;
}
public void breathe() {
System.out.println(name + "的" + position + "正在呼吸,增加氧气水平");
// 直接访问外部类的私有变量和方法
oxygenLevel += 20;
Body.this.checkOxygenLevel(); // 显式调用外部类方法
}
}
// 外部类方法:运动
public void exercise() {
System.out.println(name + "开始运动...");
Heart heart = new Heart(); // 内部类可以在外部类方法中直接创建
heart.pumpBlood();
Lung rightLung = new Lung("右肺");
rightLung.breathe();
}
// 外部类私有方法:检查氧气水平
private void checkOxygenLevel() {
System.out.println("当前氧气水平:" + oxygenLevel);
}
// 对外提供心脏对象(方式一)
public Heart getHeart() {
return new Heart();
}
}
静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问非静态的需要创建对象
public class Car {//外部类
String carName;
int carAge;
int carColor;
static class Engine {//静态内部类
String engineName;
int engineAge;
}
}
创建静态内部类对象的格式:外部类.内部类名 对象名 = new 外部类名.内部类名();
调用非静态方法的格式:先创建对象,用对象调用
调用静态方法的格式:外部类名.内部类名.方法名();
注意事项:
1.静态内部类也是成员内部类中的一种
2.静态内部类只能访问外部类中的静态变量和静态方法 ,如果想要访问非静态的需要创建对象。
创建静态内部类对象的格式:
外部类名.内部类名 对象名 = new 外部类名.内部类名();
调用静态方法的格式:
外部类名.内部类名.方法名();
public class StaticInnerClassDemo {
public static void main(String[] args) {
// 创建静态内部类对象(无需先创建外部类对象)
OuterClass.InnerClass inner = new OuterClass.InnerClass();
// 调用内部类方法
inner.printOuterStaticField(); // 访问外部类静态变量
inner.accessOuterNonStaticField(); // 通过创建外部类对象访问非静态成员
// 调用静态内部类的静态方法
OuterClass.InnerClass.printMessage();
}
}
class OuterClass {
// 外部类的静态变量
public static String staticField = "外部类静态字段";
// 外部类的非静态变量
private String nonStaticField = "外部类非静态字段";
// 外部类的静态方法
public static void staticMethod() {
System.out.println("外部类静态方法被调用");
}
// 外部类的非静态方法
private void nonStaticMethod() {
System.out.println("外部类非静态方法被调用");
}
// 静态内部类(属于成员内部类的一种)
static class InnerClass {
// 静态内部类的静态变量
public static String innerStaticField = "内部类静态字段";
// 静态内部类的非静态变量
private int innerNonStaticField = 100;
// 访问外部类静态成员
public void printOuterStaticField() {
System.out.println("访问外部类静态字段: " + staticField);
staticMethod();
}
// 访问外部类非静态成员(需先创建外部类对象)
public void accessOuterNonStaticField() {
OuterClass outer = new OuterClass();
System.out.println("通过对象访问外部类非静态字段: " + outer.nonStaticField);
outer.nonStaticMethod();
}
// 静态内部类的静态方法
public static void printMessage() {
System.out.println("静态内部类的静态方法被调用");
System.out.println("访问内部静态字段: " + innerStaticField);
System.out.println("访问外部静态字段: " + staticField);
staticMethod();
}
}
}
public class StaticInnerClassDemo {
public static void main(String[] args) {
// 创建静态内部类对象(无需先创建外部类对象)
OuterClass.InnerClass inner = new OuterClass.InnerClass();
// 调用内部类方法
inner.printOuterStaticField(); // 访问外部类静态变量
inner.accessOuterNonStaticField(); // 通过创建外部类对象访问非静态成员
// 调用静态内部类的静态方法
OuterClass.InnerClass.printMessage();
}
}
class OuterClass {
// 外部类的静态变量
public static String staticField = "外部类静态字段";
// 外部类的非静态变量
private String nonStaticField = "外部类非静态字段";
// 外部类的静态方法
public static void staticMethod() {
System.out.println("外部类静态方法被调用");
}
// 外部类的非静态方法
private void nonStaticMethod() {
System.out.println("外部类非静态方法被调用");
}
// 静态内部类(属于成员内部类的一种)
static class InnerClass {
// 静态内部类的静态变量
public static String innerStaticField = "内部类静态字段";
// 静态内部类的非静态变量
private int innerNonStaticField = 100;
// 访问外部类静态成员
public void printOuterStaticField() {
System.out.println("访问外部类静态字段: " + staticField);
staticMethod();
}
// 访问外部类非静态成员(需先创建外部类对象)
public void accessOuterNonStaticField() {
OuterClass outer = new OuterClass();
System.out.println("通过对象访问外部类非静态字段: " + outer.nonStaticField);
outer.nonStaticMethod();
}
// 静态内部类的静态方法
public static void printMessage() {
System.out.println("静态内部类的静态方法被调用");
System.out.println("访问内部静态字段: " + innerStaticField);
System.out.println("访问外部静态字段: " + staticField);
staticMethod();
}
}
}
匿名内部类本质上就是隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置
格式:
new 类名或者接口名(){
重写方法;
}
package demo04;
public interface swim {
public abstract void swim();
}
package demo04;
public class test {
public static void main(String[] args) {
new swim() {
@Override
public void swim() {
System.out.println("重写了游泳的方法");
}
};
}
}
格式的细节:
包含了继承或实现,方法重写,创建对象
整体就是一个类的子类对象或者接口的实现类对象
使用场景:
``java public class Car {//外部类 String carName; int carAge; int carColor; static class Engine {//静态内部类 String engineName; int engineAge; } }
创建静态内部类对象的格式:`外部类.内部类名 对象名 = new 外部类名.内部类名();`
调用非静态方法的格式:先创建对象,用对象调用
调用静态方法的格式:`外部类名.内部类名.方法名();`
注意事项:
1.静态内部类也是成员内部类中的一种
2.静态内部类只能访问外部类中的静态变量和静态方法 ,如果想要访问非静态的需要创建对象。
创建静态内部类对象的格式:
`外部类名.内部类名 对象名 = new 外部类名.内部类名(); `
调用静态方法的格式:
`外部类名.内部类名.方法名();`
```java
public class StaticInnerClassDemo {
public static void main(String[] args) {
// 创建静态内部类对象(无需先创建外部类对象)
OuterClass.InnerClass inner = new OuterClass.InnerClass();
// 调用内部类方法
inner.printOuterStaticField(); // 访问外部类静态变量
inner.accessOuterNonStaticField(); // 通过创建外部类对象访问非静态成员
// 调用静态内部类的静态方法
OuterClass.InnerClass.printMessage();
}
}
class OuterClass {
// 外部类的静态变量
public static String staticField = "外部类静态字段";
// 外部类的非静态变量
private String nonStaticField = "外部类非静态字段";
// 外部类的静态方法
public static void staticMethod() {
System.out.println("外部类静态方法被调用");
}
// 外部类的非静态方法
private void nonStaticMethod() {
System.out.println("外部类非静态方法被调用");
}
// 静态内部类(属于成员内部类的一种)
static class InnerClass {
// 静态内部类的静态变量
public static String innerStaticField = "内部类静态字段";
// 静态内部类的非静态变量
private int innerNonStaticField = 100;
// 访问外部类静态成员
public void printOuterStaticField() {
System.out.println("访问外部类静态字段: " + staticField);
staticMethod();
}
// 访问外部类非静态成员(需先创建外部类对象)
public void accessOuterNonStaticField() {
OuterClass outer = new OuterClass();
System.out.println("通过对象访问外部类非静态字段: " + outer.nonStaticField);
outer.nonStaticMethod();
}
// 静态内部类的静态方法
public static void printMessage() {
System.out.println("静态内部类的静态方法被调用");
System.out.println("访问内部静态字段: " + innerStaticField);
System.out.println("访问外部静态字段: " + staticField);
staticMethod();
}
}
}
public class StaticInnerClassDemo {
public static void main(String[] args) {
// 创建静态内部类对象(无需先创建外部类对象)
OuterClass.InnerClass inner = new OuterClass.InnerClass();
// 调用内部类方法
inner.printOuterStaticField(); // 访问外部类静态变量
inner.accessOuterNonStaticField(); // 通过创建外部类对象访问非静态成员
// 调用静态内部类的静态方法
OuterClass.InnerClass.printMessage();
}
}
class OuterClass {
// 外部类的静态变量
public static String staticField = "外部类静态字段";
// 外部类的非静态变量
private String nonStaticField = "外部类非静态字段";
// 外部类的静态方法
public static void staticMethod() {
System.out.println("外部类静态方法被调用");
}
// 外部类的非静态方法
private void nonStaticMethod() {
System.out.println("外部类非静态方法被调用");
}
// 静态内部类(属于成员内部类的一种)
static class InnerClass {
// 静态内部类的静态变量
public static String innerStaticField = "内部类静态字段";
// 静态内部类的非静态变量
private int innerNonStaticField = 100;
// 访问外部类静态成员
public void printOuterStaticField() {
System.out.println("访问外部类静态字段: " + staticField);
staticMethod();
}
// 访问外部类非静态成员(需先创建外部类对象)
public void accessOuterNonStaticField() {
OuterClass outer = new OuterClass();
System.out.println("通过对象访问外部类非静态字段: " + outer.nonStaticField);
outer.nonStaticMethod();
}
// 静态内部类的静态方法
public static void printMessage() {
System.out.println("静态内部类的静态方法被调用");
System.out.println("访问内部静态字段: " + innerStaticField);
System.out.println("访问外部静态字段: " + staticField);
staticMethod();
}
}
}
匿名内部类本质上就是隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置
格式:
new 类名或者接口名(){
重写方法;
}
package demo04;
public interface swim {
public abstract void swim();
}
package demo04;
public class test {
public static void main(String[] args) {
new swim() {
@Override
public void swim() {
System.out.println("重写了游泳的方法");
}
};
}
}
格式的细节:
包含了继承或实现,方法重写,创建对象
整体就是一个类的子类对象或者接口的实现类对象
使用场景:
当方法的参数是接口或者类时,以接口为例,可以传递这个接口的实现对象,如果实现类只要使用一次,就可以用匿名内部类简化代码。