类的组成:
类和对象的关系:
成员变量: 存储在对象的堆内存中。每个对象都有自己的堆内存空间,其中包含其成员变量的值。
方法调用: 方法的调用和执行发生在栈内存上。
代码运行结果:
总结:
多个对象在堆内存中,都有不同的内存划分,成员变量存储在各自的内存区域中,成员方法多个对象共用的一份
总结
当多个对象的引用指向同一个内存空间(变量所记录的地址值是一样的)
只要有任何一个对象修改了内存中的数据,随后,无论使用哪一个对象进行数据获取,都是修改后的数据。
public class VariableExample {
// 成员变量
private int memberVariable;
// 成员方法
public void exampleMethod() {
// 局部变量
int localVar1 = 10;
int localVar2 = 20;
System.out.println("Local Variable 1: " + localVar1);
System.out.println("Local Variable 2: " + localVar2);
// 使用成员变量
memberVariable = localVar1 + localVar2;
System.out.println("Member Variable: " + memberVariable);
}
public static void main(String[] args) {
// 创建对象
VariableExample exampleObject = new VariableExample();
// 调用对象的方法
exampleObject.exampleMethod();
}
}
方法(method)是将具有独立功能的代码块组织成为一个整体,使其具有特殊功能的代码集
本文仅对Java中方法做大致介绍
public static void method ( ) {
// 方法体;
}
public static void 方法名 (参数1) {
方法体;
}
public static void 方法名 (参数1, 参数2, 参数3...) {
方法体;
}
可变参数:
在Java中,如果希望一个方法接受不确定数量的参数,可以使用可变参数(Varargs)的语法。
可变参数允许你指定一个参数,其数量可以是可变的,而不需要提前声明参数的数量。在方法定义中使用省略号 … 表示可变参数。
public static void printNumbers(int... numbers) { for (int number : numbers) { System.out.print(number + " "); } System.out.println(); } public static void main(String[] args) { // 调用方法时传入不确定数量的参数 printNumbers(1, 2, 3); printNumbers(4, 5, 6, 7, 8); printNumbers(9); }
可变参数使用的注意事项:
可变参数在方法参数列表中必须是最后一个参数,而且每个方法最多只能有一个可变参数。此外,可变参数实际上被视为数组,因此方法内部可以像处理数组一样处理可变参数。
public class VariableArgumentsExample { public static void exampleMethod(String firstArg, int... numbers) { // 方法体 } }
public static 数据类型 方法名 ( 参数 ) {
return 数据 ;
}
功能:主要是完成对象数据的初始化
格式:
public class 类名{
修饰符 类名( 参数 ) {
}
}
注意:
如果没有定义构造方法,系统将给出一个默认的无参数构造方法
如果定义了构造方法,系统将不再提供默认的构造方法
如果自定义了带参构造方法,还要使用无参数构造方法,就必须再写一个无参数构造方法
建议:
无论是否使用,都手工书写无参数构造方法
可以使用带参构造,为成员变量进行初始化
setXxx和getXxx访问控制 - 封装思想
set
和 get
方法通常不被归类为构造方法。它们是一种常见的编程约定,用于提供对对象的私有属性的访问和修改。
方法重载指同一个类中定义的多个方法之间的关系,满足下列条件的多个方法相互构成重载:
正确方法重载:
public class MethodDemo {
public static void fn(int a) {
//方法体
}
public static int fn(double a) {
//方法体
}
}
public class MethodDemo {
public static float fn(int a) {
//方法体
}
public static int fn(int a , int b) {
//方法体
}
}
错误的方法重载 :
public class MethodDemo {
public static void fn(int a) {
//方法体
}
public static int fn(int a) { /*错误原因:重载与返回值无关*/
//方法体
}
}
public class MethodDemo01 {
public static void fn(int a) {
//方法体
}
}
public class MethodDemo02 {
public static int fn(double a) { /*错误原因:这是两个类的两个fn方法*/
//方法体
}
}
方法重载避免了过度的方法命名,提高了代码的可读性和简洁性,使得方法的参数的类型及个数更加的灵活
核心:
基本数据类型的参数,形式参数的改变,不影响实际参数
依据:每个方法在栈内存中,都会有独立的栈空间,方法运行结束后就会弹栈消失
引用类型的参数,形式参数的改变,影响实际参数的值
依据:引用数据类型的传参,传入的是地址值,内存中会造成两个引用指向同一个内存的效果,所以即使方法弹栈,堆内存中的数据也已经是改变后的结果
继承是面向对象三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义,以及追加属性和方法
继承通过extends
实现
格式:class 子类 extends 父类 { }
public class Fu {
public void show() {
System.out.println("show方法被调用");
}
}
public class Zi extends Fu {
public void method() {
System.out.println("method方法被调用");
}
}
public class Demo {
public static void main(String[] args) {
//创建对象,调用方法
Fu f = new Fu();
f.show();
Zi z = new Zi();
z.method();
z.show();
}
}
使用继承,需要考虑类与类之间是否存在is..a
的关系,不能盲目使用继承
is…a的关系:谁是谁的一种,例如:老师和学生是人的一种,那人就是父类,学生和老师就是子类
在子类方法中访问一个变量,采用的是就近原则。
子类局部范围找
子类成员范围找
父类成员范围找
如果都没有就报错(不考虑父亲的父亲)
Super
注意:子类中所有的构造方法默认都会访问父类中无参的构造方法
子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化,原因在于,每一个子类构造方法的第一条语句默认都是:super()
提问:如果父类中没有无参构造方法,只有带参构造方法,该怎么办呢?
1. 通过使用super关键字去显示的调用父类的带参构造方法 2. 在父类中自己提供一个无参构造方法
最好的办法仍是自己给出无参构造方法
当我们通过子类对象访问一个方法时
1、概念
2、应用场景
当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容
// 父类
class Animal {
// 方法被子类重写
public void makeSound() {
System.out.println("Animal makes a sound");
}
}
// 子类
class Dog extends Animal {
// 重写父类的方法
@Override
public void makeSound() {
System.out.println("Dog barks");
}
}
// 主方法用于测试
public class InheritanceExample {
public static void main(String[] args) {
// 创建子类对象
Dog myDog = new Dog();
// 调用被重写的方法
myDog.makeSound();
}
}
3、Override注解
什么是多态
同一个对象,在不同时刻表现出来的不同形态
多态的前提 重点
重点
成员访问特点
成员变量
编译看父类,运行看父类:编译和运行时期都是参考引用变量所属类中的成员,且在编译期间,如果没有,则编译失败,所以也不能使用子类的特有成员
成员方法
编译看父类,运行看子类:当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的是子类重写后方法
代码演示
动物类
public class Animal {
public int age = 40;
public void eat() {
System.out.println("动物吃东西");
}
}
猫类
public class Cat extends Animal {
public int age = 20;
public int weight = 10;
@Override
public void eat() {
System.out.println("猫吃鱼");
}
public void playGame() {
System.out.println("猫捉迷藏");
}
}
测试类
public class AnimalDemo {
public static void main(String[] args) {
//有父类引用指向子类对象
Animal a = new Cat();
System.out.println(a.age); //输出40
// System.out.println(a.weight);//编译失败
a.eat();//输出猫吃鱼
// a.playGame();//编译失败
}
}
重点
好处
提高程序的扩展性。定义方法时候,使用父类型作为参数,在使用的时候,使用具体的子类型参与操作
弊端
不能使用子类的特有成员
重点
向上转型 (多态中的默认过程)
父类引用指向子类对象就是向上转型
向下转型 (强制过程)
格式:子类型 对象名 = (子类型)父类引用;
代码演示
public class Animal {
public void eat() {
System.out.println("动物吃东西");
}
}
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
public void playGame() {
System.out.println("猫捉迷藏");
}
}
public class AnimalDemo {
public static void main(String[] args) {
//多态
//向上转型
Animal a = new Cat();
a.eat();
// a.playGame();
//向下转型
Cat c = (Cat)a;
c.eat();
c.playGame();
}
}
注意:必须保证对象在创建的时候就是猫,向下转型才可以成为猫;否则,如果最开始为狗,如果要将其强制转换为猫,就会报错。
当我们在做子类共性功能抽取时,有些方法在父类中并没有具体的实现,这个时候就需要抽象类
在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类
抽象类和抽象方法必须使用 abstract 关键字修饰
//抽象类的定义
public abstract class 类名 {}
//抽象方法的定义
public abstract void eat();
抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
抽象类不能实例化,参照多态的方式,通过子类对象实例化,这叫抽象类多态
抽象类的子类
要么重写抽象类中的所有抽象方法;否则自身也是是抽象类
Java中的接口更多的体现在对行为的抽象
接口用关键字interface
修饰
public interface 接口名 {}
类实现接口用implements
表示
public class 类名 implements 接口名 {}
接口不能实例化
参照多态的方式,通过实现类对象实例化,这叫接口多态。
多态的形式:具体类多态,抽象类多态,接口多态。
接口的子类
要么重写接口中的所有抽象方法
要么子类也是抽象类
接口中的常量是不能修改的
重点
类与类的关系:
继承关系,只能单继承,但是可以多层继承
类与接口的关系
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
接口与接口的关系
继承关系,可以单继承,也可以多继承
// 定义第一个接口
interface InterfaceA {
void methodA();
}
// 定义第二个接口
interface InterfaceB {
void methodB();
}
// 定义继承自两个接口的新接口
interface CombinedInterface extends InterfaceA, InterfaceB {
void combinedMethod();
}
重点
成员区别
抽象类
变量,常量;有构造方法;有抽象方法,也有非抽象方法
接口
常量;抽象方法
关系区别
类与类
继承,单继承
类与接口
实现,可以单实现,也可以多实现
接口与接口
继承,单继承,多继承
设计理念区别
抽象类
对类抽象,包括属性、行为
接口
对行为抽象,主要是行为
内部类概念
在一个类A的内部定义一个类B,类B就被称为内部类
内部类定义格式
/*
格式:
class 外部类名{
修饰符 class 内部类名{
}
}
*/
class Outer {
public class Inner {
}
}
内部类的访问特点 重点
示例代码:
/*
内部类访问特点:
内部类可以直接访问外部类的成员,包括私有
外部类要访问内部类的成员,必须创建对象
*/
public class Outer {
private int num = 10;
public class Inner {
public void show() {
System.out.println(num);
}
}
public void method() {
Inner i = new Inner();
i.show();
}
}
成员内部类的定义位置
外界创建成员内部类格式
Outer.Inner oi = new Outer().new Inner();
成员内部类的推荐使用方案
示例代码:
class Outer {
private int num = 10;
private class Inner {
public void show() {
System.out.println(num);
}
}
public void method() {
Inner i = new Inner();
i.show();
}
}
public class InnerDemo {
public static void main(String[] args) {
//Outer.Inner oi = new Outer().new Inner();
//oi.show();
Outer o = new Outer();
o.method();
}
}
局部内部类定义位置
局部内部类方式
示例代码
class Outer {
private int num = 10;
public void method() {
int num2 = 20;
class Inner {
public void show() {
System.out.println(num);
System.out.println(num2);
}
}
Inner i = new Inner();
i.show();
}
}
public class OuterDemo {
public static void main(String[] args) {
Outer o = new Outer();
o.method();
}
}
匿名内部类的前提
匿名内部类的格式
格式:new 类名 ( ) { 重写方法 } new 接口名 ( ) { 重写方法 }
举例:
new Inter(){
@Override
public void method(){}
}
匿名内部类的细节
匿名内部类可以通过多态的形式接受
Inter i = new Inter(){
@Override
public void method(){
}
}
匿名内部类直接调用方法
interface Inter{
void method();
}
class Test{
public static void main(String[] args){
new Inter(){
@Override
public void method(){
System.out.println("我是匿名内部类");
}
}.method(); // 直接调用方法
}
}
匿名内部类在开发中的使用
通常用于简化代码
当发现某个方法需要,接口或抽象类的子类对象,我们就可以传递一个匿名内部类过去,来简化传统的代码
示例代码:
interface Jumpping {
void jump();
}
class Cat implements Jumpping {
@Override
public void jump() {
System.out.println("猫可以跳高了");
}
}
class Dog implements Jumpping {
@Override
public void jump() {
System.out.println("狗可以跳高了");
}
}
class JumppingOperator {
public void method(Jumpping j) { //new Cat(); new Dog();
j.jump();
}
}
class JumppingDemo {
public static void main(String[] args) {
//需求:创建接口操作类的对象,调用method方法
JumppingOperator jo = new JumppingOperator();
Jumpping j = new Cat();
jo.method(j);
Jumpping j2 = new Dog();
jo.method(j2);
System.out.println("--------");
// 匿名内部类的简化
jo.method(new Jumpping() {
@Override
public void jump() {
System.out.println("猫可以跳高了");
}
});
// 匿名内部类的简化
jo.method(new Jumpping() {
@Override
public void jump() {
System.out.println("狗可以跳高了");
}
});
}
}