JAVA 入门第二季 面向对象

本文深入讲解Java中的类和对象概念,探讨成员变量与局部变量的区别,构造方法的使用,以及静态成员的特点。此外,还详细介绍了封装、继承、多态等核心特性,并解析了抽象类与接口的应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

继承、封装、多态

第1章类和对象

成员变量与局部变量区别
1、作用域不同
局部变量的作用域仅限于定义他的方法。
成员变量的作用域在整个类内部都是可见的
2、初始值不同
Java会给成员变量一个初始值(默认为0,String默认为null)
Java不会给局部变量赋予一个初始值(不赋值会提示报错)
3、在同一个方法中不允许有同名局部变量
在不同的方法中,可以有同名局部变量
4、两类变量同名时,局部变量具有更高的优先级(就近原则—方法内部:局部变量会覆盖成员变量)

public class HelloWorld { 
    int a;
	String str="成员变量";// 声明成员变量
   
    public void asy() {
		 str="hello world";//相当于为成员变量赋值
		 //如果 成员变量str为final会很明显看出为其赋值时报错。
		 
	     //String str="hello world";仅仅相当于声明了一个重名的局部变量而已。与成员变量的原str无关
    	 System.out.println("asy()中str:"+str);
    	 System.out.println("asy()中a:"+a);
    }    
	 public void asy2(){  
		 //str="再次被修改"; 	输出asy2()中str:再次被修改
    	System.out.println("asy2()中str:"+str);
    	System.out.println("asy2()中a:"+a);
    }
    
    public static void main(String[] args) {            
        HelloWorld hello = new HelloWorld();
        hello.asy();
        hello2.asy2();     
    }
}
//1:如果 成员变量a和str都不赋值的时候  输出a:0 str:null(成员变量会默认赋值)

//2:asy()方法中String str="hello world"; (局部变量具有更高的优先级)
输出:
asy()中str:hello world
asy()中a:10
asy2()中str:成员变量
asy2()中a:0

//3、如果 成员变量str在asy()中赋值str="hello world";则原成员变量的值被覆盖(重新指向)
asy()中str:hello world
asy()中a:10
asy2()中str:hello world
asy2()中a:0

构造方法
1.使用new+构造方法 创建一个新的对象(创建对象的时候,执行的是构造方法)
2.构造方法是定义在Java类中的一个用来初始化对象的方法
构造方法与类同名且没有返回值

//没有返回值
//
public 构造方法名(可以指定参数【是为了初始化成员变量,赋值】,也可以是无参的){//构造方法名与类名相同
//初始化代码
}

new+构造方法

public class Telphone {
	private String name;
	private float screen;
//当没有指定构造方法时,系统会自动添加无参的构造方法
//当有指定构造方法时,无论有参、无参的构造方法,将不会自动添加无参的构造方法
	public Telphone(){
		System.out.println("无参的构造方法");
	}	
	//构造方法不但可以给对象的属性赋值,还可以保证给对象的属性赋一个合理的值
	public Telphone(String band,float newScreen){
		name=band;//给对象的属性赋值
		if(newScreen<3.5f){
		System.out.println("输入的屏幕尺寸过小,自动赋默认值3.5f");
		screen=3.5f;
		}else{
		screen=newScreen;
		}
		System.out.println("有参的构造方法,入参name:"+name);
	}
}
public static void main(String[] args) {
		//通过无参的构造方法创建对象
		Telphone phone=new Telphone();//会打印出:无参的构造方法
		//通过有参的构造方法创建对象
		Telphone phone2=new Telphone("华为",1.5f);//输出结果:
		//输入的屏幕尺寸过小,自动赋默认值3.5f
		//有参的构造方法,入参name:华为
	}

构造方法的重载:
方法名相同
参数不同
调用时会根据不同的参数选择相应的方法。

static 使用之静态变量
Java 中被 static 修饰的成员称为静态成员或类成员。它属于整个类所有,而不是某个对象所有,即被类的所有对象所共享。静态成员可以使用类名直接访问,也可以使用对象名进行访问。

使用 static 可以修饰变量、方法和代码块。
这里写图片描述

输出结果
通过类名访问hobby:imooc
通过对象名访问hobby:imooc
通过类名访问hobby:"爱慕课"(被修改了,注意与final的区别)

静态成员属于整个类,当系统第一次使用该类时,就会为其分配内存空间直到该类被卸载才会进行资源回收!

静态方法
1、 静态方法中可以直接调用同类中的静态成员,但不能直接调用非静态成员。
如果希望在静态方法中调用非静态变量,可以通过创建类的对象,然后通过对象来访问非静态变量。

2、 在普通成员方法中,则可以直接访问同类的非静态变量和静态变量

3、 静态方法中不能直接调用非静态方法,需要通过对象来访问非静态方法。( 静态方法中能直接调用静态方法)

静态初始化块只在类加载时执行,且只会执行一次,同时静态初始化块只能给静态变量赋值,不能初始化普通的成员变量。

public class HelloWorld { 
    String name; // 声明变量name
	String sex; // 声明变量sex
	static int age;// 声明静态变量age   
    // 构造方法
	public HelloWorld() { 
		System.out.println("通过构造方法初始化name");
		name = "tom";
	}
    // 初始化块
	{ 
		System.out.println("通过初始化块初始化sex");
		sex = "男";
	}    
    // 静态初始化块
	   static { 
		System.out.println("通过静态初始化块初始化age");
		age = 20;
	}    
	public void show() {
		System.out.println("姓名:" + name + ",性别:" + sex + ",年龄:" + age);
	}    
	public static void main(String[] args) {       
       System.out.println("创建hello对象");
        // 创建对象
		HelloWorld hello = new HelloWorld();
		// 调用对象的show方法
        hello.show();
        
        System.out.println("创建hello2对象");
        HelloWorld hello2 = new HelloWorld();  
        hello2.show();     
	}
}
输出结果:
通过静态初始化块初始化age
创建hello对象
通过初始化块初始化sex
通过构造方法初始化name
姓名:tom,性别:男,年龄:20

创建hello2对象
通过初始化块初始化sex
通过构造方法初始化name
姓名:tom,性别:男,年龄:20

静态初始化块–>普通初始化块–>构造方法。
由于静态初始化块只在类加载时执行一次,所以当再次创建对象hello2 时并未执行静态初始化块。

封装

**1.概念:**将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问
2、好处:
a。只能通过规定的方法访问数据
b。隐藏类的实例细节,方便修改和实现
3、封装的实现步骤
修改属性的可见性——设为private
创建getter/setter方法——用于属性的读写
在getter/setter方法中加入属性控制语句——对属性值的合法性进行判断

访问修饰符

访问修饰符本类同包子类其他
private
默认
protected
public

这里写图片描述

this关键字
this关键字代表当前对象
this.属性 表示当前对象的属性
this.方法 表示当前对象的方法

封装对象的属性的时候,常常会使用this关键字

**内部类( Inner Class )**就是定义在另外一个类里面的类。与之对应,包含内部类的类被称为外部类。

问:那为什么要将一个类定义在另一个类里面呢?清清爽爽的独立的一个类多好啊!!
答:内部类的主要作用如下:

  1. 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类
  2. 内部类的方法可以直接访问外部类的所有数据,包括私有的数据
  3. 内部类所实现的功能使用外部类同样可以实现,只是有时使用内部类更方便
//外部类HelloWorld
public class HelloWorld {   
    // 内部类Inner,类Inner在类HelloWorld的内部
    public class Inner {        
		// 内部类的方法
		public void show() {
			System.out.println("welcome to imooc!");
		}
	}    
	public static void main(String[] args) {       
        // 创建外部类对象
		HelloWorld hello = new HelloWorld();
        // 创建内部类对象
		Inner i = hello.new Inner();
        // 调用内部类对象的方法
		i.show();
	}
}

内部类可分为以下几种:

  • 成员内部类 --也称为普通内部类
  • 静态内部类
  • 方法内部类
  • 匿名内部类

成员内部类

//外部类Outer
public class Outer{
    //外部类的私有属性name
    private String name = "imooc";
    //外部类的成员属性
    int age = 20;
    
	/*1、成员内部类Inner定义在 Outer 类的内部,相当于 Outer 类的一个成员变量的位置,
	Inner 类可以使用任意访问控制符,如 public 、 protected 、 private 等*/
	public class Inner {
		String name = "爱慕课";
        //内部类中的方法
		public void show() { 
	    /* 2、Inner 类中定义的show() 方法可以直接访问 Outer 类中的数据,而不受访问控制符的影响,
	    如直接访问 Outer 类中的私有属性age,name;*/
	    	System.out.println("外部类中的age:" + age);
		  /*如果外部类和内部类具有相同的成员变量或方法,内部类默认访问自己的成员变量或方法,
			如果要访问外部类的成员变量,可以使用 this 关键字。*/
			System.out.println("外部类中的name:" +Outer.this.name);
			System.out.println("内部类中的name:" + name);
		}
	} 
	//测试成员内部类
	public static void main(String[] args) {
        //创建外部类的对象
		Outer o = new Outer (); 
	    /*3、 定义了成员内部类后,必须使用外部类对象来创建内部类对象,而不能直接去 new 一个内部类对象,
	    即:【内部类 对象名 = 外部类对象.new 内部类( );】*/
		Inner inn = o.new Inner();
        //调用内部类对象的show方法
		inn.show();
	}
}

4、 编译上面的程序后,会发现产生了两个 .class 文件:
Out$Inner.class
Outer.class
其中,第二个是外部类的 .class 文件,第一个是内部类的 .class 文件,即成员内部类的 .class 文件总是这样:外部类名$内部类名.class

注意
1、 外部类是不能直接使用内部类的成员和方法
可先创建内部类的对象,然后通过内部类的对象来访问其成员变量和方法。

静态内部类
static 修饰的内部类

//外部类
public class HelloWorld {
    // 外部类中的静态变量score
    private static int score = 84;
    private static int b = 99;
   // 外部类中的非静态变量a
    private String a="非静态成员";
    // 创建静态内部类
	public static class SInner {
        // 内部类中的变量score
        int score = 91;
		public void show() {
		/*2、如果外部类的静态成员与内部类的成员名称相同,可通过“类名.静态成员”访问外部类的静态成员;
		如果外部类的静态成员与内部类的成员名称不相同,则可通过“成员名”直接调用外部类的静态成员*/
		System.out.println("访问外部类中的score:" +HelloWorld.score);
		System.out.println("访问外部类中的b:" +b);
		/* 1、静态内部类不能直接访问外部类的非静态成员,但可以通过 new 外部类().成员 的方式访问 */
		System.out.println("访问外部类中的非静态成员a:" + new HelloWorld().a);	
		System.out.println("访问内部类中的score:" + score);
		}
	}

	// 测试静态内部类
	public static void main(String[] args) {
		 /*3、创建静态内部类的对象时,不需要外部类的对象,可以直接创建 内部类 对象名= new 内部类();*/
        SInner si=new SInner();
        // 调用show方法
		si.show();
	}
}
输出结果:
访问外部类中的score:84
访问外部类中的b:99
访问外部类中的非静态成员a:非静态成员
访问内部类中的score:91

方法内部类
☞内部类定义在外部类的方法中,方法内部类只在该方法的内部可见,即只在该方法内可以使用。

//外部类
public class HelloWorld {
    private String name = "爱慕课";
    // 外部类中的show方法
    public void show() { 
		// 定义方法内部类【内部类定义在外部类的方法中,且只在该方法内可以使用】
		class MInner {
			int score = 83;
			public int getScore() {
				return score + 10;
			}
		}
		// 创建方法内部类的对象
        MInner mi=new MInner();
        // 调用内部类的方法
        int newScore=mi.getScore();		
		System.out.println("姓名:" + name + "\n加分后的成绩:" + newScore);
	}
    
	// 测试方法内部类
	public static void main(String[] args) {
		// 创建外部类的对象
        HelloWorld mo=new HelloWorld();
        // 调用外部类的方法
		mo.show();
	}
}

注意:由于方法内部类不能在外部类的方法以外的地方使用,因此方法内部类不能使用访问控制符和 static 修饰符。

练习题

继承

1.继承的概念:
继承是类与类的一种关系,是一种"is a"的关系;
注:Java中的继承是单继承(也就是一个类只有一个父类)
2.继承的好处:
子类拥有父类的所有属性和方法(属性和方法的修饰符不能是private,private的属性和方法是不能继承的)

修饰符 class 子类 extends 父类
例如
public class  Dog extends Animal{

 }

方法的重写:
如果子类对继承父类的方法不满意,是可以重写父类继承的方法的,调用方法时会优先调用子类的方法。
规则:

  • 返回值类型
  • 方法名
  • 参数类型及个数

都要与父类继承的方法相同,才叫方法的重写。

继承的初始化顺序
1,初始化父类再初始化子类
2,先执行初始化对象中的属性,再执行构造方法中的初始化
父类

public class Animal {
	public int age=10;
	public String name;
	public void eat(){
		System.out.println("动物具有吃的能力");
	}
	public Animal(){
		System.out.println("父类Animal执行构造方法");
		age=20;
	}
}

子类

public class Dog extends Animal{
	public void eat(){
		System.out.println("狗具有吃的能力");
	}
	public Dog(){
		//super();隐试写了一个父类的构造方法。如果显示调用,必须在子类构造方法的第一行
		System.out.println("子类Dog执行构造方法");
	}
}

测试

public static void main(String[] args) {
		Dog dog=new Dog();
		dog.eat();
//输出:
父类Animal执行构造方法
子类Dog执行构造方法
狗具有吃的能力
		Animal animal=new Animal();
		System.out.println("Animal age:"+animal.age);
//输出:
父类Animal执行构造方法
Animal age:20(将原来的属性值10覆盖掉了,所以是先属性初始化)
	}

顺序

继承

final关键字放在访问修饰符之前。

选择题
如果用 final 修饰一个类的话,代表此类不可被继承。
不是静态类
能被实例化
能继承其他类

super关键字:
在对象的内部使用,可以代表父类对象
1、访问父类的属性
super.age
2、访问父类的方法
super.eat()

  • 子类的构造过程中必须调用父类的构造方法
  • 如果子类的构造方法中没有显示调用父类的构造方法,则系统默认调用父类无参的构造方法
  • 如果显示的调用构造方法,必须在子类的构造方法的第一行
  • 如果子类构造方法中既没有显示调用父类的构造方法,而父类又没有无参的构造方法(当有指定构造方法时,无论有参、无参的构造方法,将不会自动添加无参的构造方法),则编译出错。

Object 类
object类是所有类的父类,如果一个类没有使用extends关键字明确标示 继承另外一个类,那么这个类默认继承object类。
object类中的所有方法,适合所有子类。
1.toString()方法
在object类里面定义toString()方法的时候返回的对象的哈希code码(对象地址字符串)
可以通过重写toString()方法表示出对象的属性
2.equals()方法
比较的是对象的引用是否指向同一块内存地址
Dog dog = new Dog()
一般情况下比较两个对象是比较他的是否一致,所以要进行重写。

Dog 类中的age属性
@override
public boolean equals(Object obj) {
		if (this == obj)//引用的值(地址)是否相同【相等表示指向同一个对象】
			return true;
		if (obj == null)//不会比较2个null对象的 一个为null一个不为null肯定不相等
			return false;
		if (getClass() != obj.getClass())//判断2个对象的类型
			return false;
		Dog other = (Dog) obj;
		if (age != other.age)//属性值比较
			return false;
		return true;
	}

类的对象:关心对象属性值的信息-----数据信息 。 类对象:类的代码信息(属性,变量名,方法)
类对象与类的对象
没有重写 toString 方法的情况下直接用 print 打印对象,输出的是此对象的地址。
默认继承 Object 的 toString( ) 方法,输出对象地址。

多态

【继承是多态实现的基础】
对象的多种形态
1.引用多态
父类的引用可以指向本类的对象
父类的引用可以指向子类的对象【子类的引用不能指向父类的对象】
2.方法多态
创建本类对象时,调用的方法为本类方法
创建子类对象时,调用的方法为子类重写的方法或者继承的方法
不能通过父类的引用调用子类独有的方法

引用类型转换
1.向上(隐式/自动)小类型到大类型
2.向下(强制)大类型到小类型【有风险:数据溢出】
3.instanceof运算符,来解决引用对象的类型,避免类型转换的安全性问题【返回的是boolean值】

if(类 instanceof 类){
//例如 if(animal instanceof cat)//animal是父类,cat是子类
//如果动物对象中含有Cat元素,则可以转换---->true
}

抽象类
1.语法定义
抽象类前使用abstract关键字修饰,则该类为抽象类。
2.应用场景:
a.在某些情况下,某个父类只是知道其子类应该包含怎样的方法,但无法准确知道这些子类如何实现这些方法。
b.从多个具有相同特征的类中抽象出一个抽象类,以这个抽象类作为子类的模板,从而避免了子类设计的随意性。
3.作用:
限制规定子类必须实现某些方法,但不关注实现细节。
4.使用规则
定义抽象类
定义抽象方法【抽象方法没有方法体】,只有声明,不需要实现。
包含抽象方法的类是抽象类
抽象类中可以包含普通的方法,也可以没有抽象方法
抽象类不能直接创建,可以定义引用变量

包含抽象方法的类一定是抽象类,抽象类和抽象方法都需要添加关键字 abstract,且顺序为 abstract class
接口中方法不能有方法体,同时方法的访问修饰符不能是 private 和 protected

父类

public abstract class Telphone {
	public abstract void call();
	public abstract void message();
	}

子类

public class Cellphone extends Telphone{
	@Override
	public void call() {
		System.out.println("通过键盘来打电话");
	}
	@Override
	public void message() {
		System.out.println("通过键盘来发信息");		
	}
}
public class SmartPhone extends Telphone {
	@Override
	public void call() {
		System.out.println("通过语音来打电话");
	}
	@Override
	public void message() {
		System.out.println("通过语音来发短信");
	}
}

测试

		Telphone tel=new Cellphone();
		tel.call();
		tel.message();
		
		Telphone tel2=new SmartPhone();
		tel2.call();
		tel2.message();
//输出
通过键盘来打电话
通过键盘来发信息

通过语音来打电话
通过语音来发短信

接口

接口可以理解为一个特殊的类,由全局常量和公共的抽象方法组成

类是一种具体实现体,而接口定义了某一批类所需要遵守的规范,接口不关心这些类的内数据,也不关心这些类里方法的实现细节,它只规定这些类里必须提供某种方法。

定义类使用class,定义接口使用interface

【访问修饰符】【abstract如果没写,系统会默认加上】 interface 接口名【extends 父接口1,父接口2】{
0个到多个常量定义。。。。。
【属性是常量,即使定义时不添加 public static final修饰符,系统也会自动加上】
0个到多个抽象方法的定义。。。。
【接口中的方法只能是抽象方法,总是使用,即使定义时不添加public abstract 修饰符,系统也会自动加上】
}

接口是用来被继承、被实现的,访问修饰符一般建议使用public
不能使用private和protected

接口使用
一个类可以实现一个或多个接口,实现接口使用implements。Java中一个类只能继承一个父类,是不够灵活的,通过实现多个接口可以做补充

修饰符 class 类名 extends 父类 implements 接口1,接口2...
{
类体部分//如果继承了抽象类,需要实现继承的抽象方法;要实现接口中的抽象方法
}
如果要继承父类(extends 父类),继承父类必须在实现接口(implements 接口)之前

接口

定义一个接口

//用户如果没有手动写上(abstract )的话,系统都会自动添加。一般abstract通常都不写
public  interface IpayGame {//接口都是抽象类abstract
	//属性都是static final
	public void payGame();//方法也是abstract
}
public class Psp implements IpayGame {
	@Override
	public void payGame() {
		System.out.println("Psp具有了玩游戏的功能");
	}
}
public class SmartPhone extends Telphone implements IpayGame{
	@Override
	public void call() {
		System.out.println("通过语音来打电话");
	}
	@Override
	public void message() {
		System.out.println("通过语音来发短信");
	}
	@Override
	public void payGame() {
		System.out.println("SmartPhone具有了玩游戏的功能");
	}
}

测试

//接口引用===指向===一个实现的对象
//接口的引用(IpayGame ip1)可以指向一个实现接口的对象(new SmartPhone())
IpayGame ip1=new SmartPhone();
ip1.payGame();
IpayGame ip2=new Psp();
ip2.payGame();
输出:
SmartPhone具有了玩游戏的功能
Psp具有了玩游戏的功能

匿名内部类:没有名字的内部类。多用于关注实现而不关注实现类的名称
接口在使用过程中常与匿名内部类配合使用。

Interface i=new Interface(){
    public void method(){
        System.out.println("匿名内部类实现接口的方式");
};

测试

IpayGame ip3=new IpayGame(){
	public void payGame() {
		 System.out.println("使用匿名内部类的方式实现接口 方式1");
	}
};
ip3.payGame();
//直接new接口也可以调用方法
new IpayGame(){
	public void payGame() {
		 System.out.println("使用匿名内部类的方式实现接口 方式2");
	}
}.payGame();
//输出:
使用匿名内部类的方式实现接口 方式1
使用匿名内部类的方式实现接口 方式2		

UML简介

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值