Java面向对象(二):类

本文深入解析Java中类与对象的概念、创建与使用方法,并通过示例代码详细讲解类的属性、方法、构造器等内容,帮助读者理解面向对象编程的核心思想。

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

类的定义
把同一类的属性和方法进行统一写在同一个容器中,这个容器就是类,类就是把相似属性和方法进行统一包装管理。

语法格式
[< 修饰符>] class < 类名> {
[<属性声明>]
[<构造器声明>]
[<方法声明>]
}

说明:类的修饰符只有两种一种是默认(不写)、还有一种就是public。

创建类的内存分析
在这里插入图片描述

堆区:
1.存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)
2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身 。

栈区:
1.每个线程包含一个栈区,栈中只保存基础数据类型的值和对象以及基础数据的引用
2.每个栈中的数据(基础数据类型和对象引用)都是私有的,其他栈不能访问。
3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。

方法区:
1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的类信息、常量和static变量。
2.方法区中包含的都是在整个程序中永远唯一的元素,如类信息,static变量

分析以下代码内存结构

Person p1 = new Person();
P1.name =“张三”;
p1.age = 23;
Person p2 = new Person();
p2.name = “李四”;
Person p3 = p1;

在这里插入图片描述

一、属性声明(成员变量)

语法格式:
[<修饰符>] 类型 < 属性名> [=初值] ;
说明:修饰符可以使用四个:public、默认、protected、private

成员变量和局部变量的区别

1.定义的位置不同

  • 成员变量:定义在类里面方法外部
  • 局部变量:定义在方法内部或作为参数

2.在内存中的位置不同

  • 成员变量:存储在堆内存的对象中
  • 局部变量:存储在栈内存的方法中

3.生命周期不同

  • 成员变量:随对象的出现而出现在堆中,随着对象的消失而从堆中消失
  • 局部变量:随着方法的运行而出现在栈中,随着方法的结束而从栈中消失

4.初始化不同

  • 成员变量:在堆中有默认的初始值
  • 局部变量:没有默认的初始值,必须手动赋值才可以使用

5.访问权限修饰符不同

  • 成员变量:可以使用权限修饰符4种都可以
  • 局部变量:不可以使用修饰符

二、定义方法

语法格式
<修饰符> <返回类型> <方法名>([< 参数表>]) {
[< 语句>]
}

说明:
修饰符:四个都可以使用
返回值类型:基本类型和引用类型都可以,没有返回值:void。
方法返回值:

  • 有返回值:返回值位置写具体的数据类型,表示该方法返回的数据类型,方法体中必须有return语句。
  • 无返回值:方法无返回值时,写void关键字,可以没有return语句,如果有return语句,那么return后不要写任何值,只表示方法结束并返回。

三、对象的产生

当一个对象被创建时,会对其中各种类型的成员变量自动进行初始化赋值。除了基本数据类型之外的变量类型都是引用类型。
在这里插入图片描述
创建新的对象之后,我们就可以使用”对象名.对象成员”的格式,来访问对象的成员。

匿名对象

我们也可以不定义对象的句柄,而直接调用这个对象的方法。这样的对象叫做匿名对象, 如:new Person().shout(); 如果对一个对象只需要进行一次方法调用,那么就可以使用匿名对象。我们经常将匿名对象作为实参传递给一个函数调用。

方法的定义和使用的注意事项
 a: 方法不能定义在另一个方法的里面;
 b: 写错方法名字;
 c: 写错了参数列表;
 d: 方法返回值是void,方法中可以省略return不写,return后面不能有代码;
 e: 方法返回值类型,和return后面数据类型必须匹配;
 f: 方法重复定义问题;
 g: 调用方法的时候,返回值是void, 不能写在输出语句中;

四、方法的重载

 在同一个类中可以定义多个同名方法,即方法的重载(overload)
 重载方法的参数列表必须不同;
 重载方法的返回值类型可以相同,也可以不同;
 调用时根据方法的参数类型来区别;

 方法的重载于下列情况无关
 修饰符
 返回值类型
 形参的名字
 方法体

五、可变参数的方法

Java1.5增加了新特性:
可变参数:适用于参数个数不确定,类型确定的情况,Java把可变参数当做数组处理。

 注意:可变参数必须位于最后一项,一个方法只能有一个可变参数。

 原因:因为参数个数不定,所以当其后边还有相同类型参数时,java无法区分传入的参数属于前一个可变参数还是后边的参数,所以只能让可变参数位于最后一项。

可变参数的特点:
(1)只能出现在参数列表的最后;
(2)…位于变量类型和变量名之间,前后有无空格都可以;
(3)调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体中以数组的形式访问可变参数。

六、方法参数值传递机制(重点)

方法参数值传递机制:
方法的参数传递
1.形参:方法声明时,方法小括号内的参数
实参:调用方法时,实际传入的参数的值

2.规则:Java中的参数传递机制:值传递机制
1)形参是基本数据类型的:将实参的值传递给形参的基本数据类型的变量
2)形参是引用数据类型的:将实参的引用类型变量的值(对应的堆空间的对象实体的首地址值)传递给形参的引用类型变量。

七、常见面试题

 定义一个数int[]x = new int[]{10,20,200,40,75,33,88}让数组中的每个元素值去除以数组首个元素,得到的结 果,作为数组的该位置上 的新的值,然后遍历输出新的数组。
答案:

public static void main(String[] args) {
		int[] x = new int[]{10,20,200,40,75,33,88};
		
		for (int i = x.length-1; i >= 0; i--) {
			x[i]=x[i]/x[0];
		}
		
		for(int i:x) {
			System.out.println(i);
		}

分析:

这个题目是现场笔试的题目,所以是要手写代码的。
在这里需要注意的坑有两个:
1.数组的长度是用属性length来获得的,是没有()括号的,有括号的是字符串的length()方法
2.需要逆序去除以第一个数x[0],正序直接除,会导致一开始就x[0]=x[0]/x[0];直接把原来x[0]:10,赋值为了1,后面数组的数都除的便是1,等于它本身的值,导致没有完成任务,而粗心丢分。
本题不单单只有一种解法,也可以定义一个变量先把x[0]的值保存,在一个个的除以这个变量。


  • 在这里插入图片描述
    答案:
public static void method(int a,int b) {
		
		System.out.println("a=100,b=200");
		System.exit(0);
	}
	public static void main(String[] args) {
		int a=10;
		int b=20;
		
		method(a,b);
		
		System.out.println("a="+a+",b="+b);
		
	}

分析:
这个题目巨坑,别抱怨,我们是去找工作的,我们就认了吧,阿巴阿巴.
这个题目,其实考的就是基础,基础不扎实的同学,题目看完可能就立刻跳进了这个大坑了,要记住java中的传递都是值传递,没有什么所谓的引用传递,更何况这里的a和b都是int(基本类型)的局部变量,更不可能得到对应的地址,想真实改变a和b的值,是不可能的了,居然无法改变,那就不改变它了,直接输出题目想要的结果,然后System.exit(0);退出程序即可。
要是在笔试中真遇到类似这样很坑的题目(除线上笔试情况),像是题目出错了的,就不要瞎想瞎写,这种情况不写比写错更加分,笔试完接着面试的时候,面试官问起你,你就说觉得题目出错了,把自己的理由说出来,这时你没写这题也已经给自己加分了。

 下面程序输出什么?
在这里插入图片描述
答案:

public static void main(String[] args) {
		int [] i= {2,3,4,5,6};
		
		System.out.println(i);//[I@15db9742
		
		char [] ch= {'a','b','c'};
		System.out.println(ch);//abc
		
	}

分析:
这个题目就是考察了你有没有看过println()方法的源码了,因为javinta给char[]数组开了一个后门,它直接把内容给你输出来了,而不是首地址。
int[] i;属于引用类型,接下来我直接把源码方法可以自己看一下为什么输出的是这个答案。
源码看不懂无所谓,记住println()方法给char[]开了后面,可以直接输出值就好了。

//这是int[]的源码
public void println(Object x) {
        String s = String.valueOf(x);
        synchronized (this) {
            print(s);
            newLine();
        }
    }
public void print(String s) {
        if (s == null) {
            s = "null";
        }
        write(s);
    }
 private void write(String s) {
        try {
            synchronized (this) {
                ensureOpen();
                textOut.write(s);
                textOut.flushBuffer();
                charOut.flushBuffer();
                if (autoFlush && (s.indexOf('\n') >= 0))
                    out.flush();
            }
        }
        catch (InterruptedIOException x) {
            Thread.currentThread().interrupt();
        }
        catch (IOException x) {
            trouble = true;
        }
    }


//这个是char[]的源码
public void println(char x[]) {
        synchronized (this) {
            print(x);
            newLine();
        }
    }

public void print(char s[]) {
        write(s);
    }

private void write(char buf[]) {
        try {
            synchronized (this) {
                ensureOpen();
                textOut.write(buf);
                textOut.flushBuffer();
                charOut.flushBuffer();
                if (autoFlush) {
                    for (int i = 0; i < buf.length; i++)
                        if (buf[i] == '\n')
                            out.flush();
                }
            }
        }
        catch (InterruptedIOException x) {
            Thread.currentThread().interrupt();
        }
        catch (IOException x) {
            trouble = true;
        }
    }

 下面程序输出什么?
在这里插入图片描述
答案:

public void change(String s,char[]c) {
		s="test String";
		c[0]='b';
	}
	
	public static void main(String[] args) {
		Test4 test4=new Test4();
		
		String s="test";
		
		char[]c = new char[]{'t','e','s','t'};
		
		test4.change(s, c);
		
		System.out.println(s);//test
		System.out.println(c);//best
		
	}

分析:
这里考查的是字符串的存储特点:一经创建,不可修改。test4.change(s, c);这里传递的值分别是s:方法区中字符串"test"的地址,c:堆中c数组的首地址,传递的都是地址,可是接受了之后,一开始局部变量s的值是传递过来的地址,也就是字符串“test”的地址,可是当s=“test String”;这时局部变量s的地址就改变了,保存的是字符串"test Sting"的地址,是直接改变变量的地址,而不是改变变量的值;而c[]数组则就不一样了,传给局部变量c的是数组的首地址,当c[0]='b’时,就是把对应地址上的值给修改了,所以原来的值就会跟随发生改变。
这里涉及到了内存的存储区的问题,需要有一定的内存存储的基础才能更好的理解,如果实在理不到,那就回去补补内存结构的知识吧,本篇中一开始就讲了内存结构。

 下面程序输出什么?

在这里插入图片描述
答案:

public static void main(String[] args) {
		String s1="你好北京";
		String s2="你好";
		
		String s3=s2+"北京";
		
		System.out.println(s1==s3);//false
		
		final String s4="你好";
		
		String s5=s4+"北京";
		
		System.out.println(s1==s5);//true
		
	}

分析:题目和答案的代码没有完全一致,不过题目的题意是没有任何变化的,不用担心题目和答案不同的问题。
这也是考察了字符串的存储特点:一经创建不可改变的原理。
不理解可以阅读此篇文章,有重点分析这个知识点。

八、递归方法

什么是递归?递推和回归,方法的自调用,方法自己调用自己,

  • 所以递归方法必须有一个结束条件,否则就会出现死循环
  • 除非必要,否则不推荐使用递归方法,因为效率低

 实例:输出斐波那契数列的第N个数
 0、1、1、2、3、5、8、13、21、34、……
答案:
在这里插入图片描述

结束语

继续学习点击面向对象:封装
有不理解或者GTQ28写错的都可以留言哟~~~
拜拜啦~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你不懂、、、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值