类
类的定义
把同一类的属性和方法进行统一写在同一个容器中,这个容器就是类,类就是把相似属性和方法进行统一包装管理。
语法格式:
[< 修饰符>] 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写错的都可以留言哟~~~
拜拜啦~~~