类和对象
类和对象
1. oop(object oriented programming):面向对象编程
把构成问题的事物都抽象成类,事物的行为特征抽象类的方法,事物的属性特征抽象类的属性,定义成程序的一种数据类型,通过调用对象的行为来实现功能。
2. java三大特征
封装(encapsulation) 、继承(inheritance)、多态(polymorphism)
3. 类
类是对象的抽象,对象是类的具体化。
//补充:java中的注释:
//这是一个单行注释
/*
这是一个
多行注释
*/
/**
*这是一个文档注释
*/
//写一个类
修饰符 class 类名{
}
public class Student{
public String name = "hello";//类中成员变量的定义
public void sayHello(){ //类中方法的定义
System.out.println("hello hello");
}
}
public static void main(String[] args){
//声明一个Student类变量
Student stu;
//该变量的使用需要一个具体化的Student数据,也就是一个Student对象
stu = new Student();
}
4.对象
类是对象的抽象,对象是类的具体化。面向对象的编程思想中,一切皆对象。
public static void main{
//创建一个对象
Student stu = new Student();
//调用对象的属性
System.out.println(stu.name);
//调用对象的方法
stu.getName();
}
this关键字:
this关键字是一个引用,指向当前对象,(用到哪个对象,指向哪个对象)也就是说this存储了当前对象的地址.如图:
this关键字的作用:
1. 在本类方法中调用该类的另一个成员方法或使用成员变量
public class Student{
public String name = "张三";
//用this区分局部变量和成员变量(当局部变量名和成员变量名一致时)
//如果没有变量冲突是this.name可以写成name,是一样的
public void setName(String name){
System.out.println(name);//这个打印的是传进来的参数
System.out.println(this.name)//这个打印的是成员变量那么,也就是张三
this.name = name;
}
public void study(){
System.out.println("我在学习");
}
public void work(){
this.study(); //调用本类的成员方法
System.out.println("我的其他工作");
}
}
2.在本类构造方法中调用该类另一个构造方法
public class Student{
public String name;
public Student(){
//调用一个参数的构造器,参数的类型是String
this("tom");
}
public Student(String name){
this.name = name;
}
}
tip:不能在普通方法调用构造方法,构造方法中的this语句必须放在非注释性语句第一行。
5. 引用
什么是引用?
引用就是引用类型的变量,简称引用。该变量是一个对象地址,指向对 象。
public static void main(String[] args){
//创建了一个对象,但没有引用
new Student();
//stu是一个引用,指向创建的Student类对象
Student stu = new Student();
}
如果没给对象起名字,也就是说该对象没有引用,那么这个对象能使用吗?
答案是肯定的,没有引用的对象创建了在堆中同样分配内存,但是只能使用一次,因为他没有名字,无法在语法上表示,只能在创建时使用。
如:
public static void main(String[] args){
System.out.println(new Student());
}
引用、对象、类之间的关系?
打一个比喻吧,假设类是空调的图纸,对象就是实体的空调,而引用就是空调遥控器。
6. 内存中对象的创建是怎样的?
public static void main(String[] args){
Student stu = new Student();
}
上述代码执行的内存变化:
- 以上为jdk1.7内存模型,类文件从硬盘中加载到方法区后,执行类文件,
- 找到程序执行的入口(main方法)开始运行
- 在栈中main方法压栈,
- 创建类型为Student的属性stu
- 当new Student()时,在堆区中创建了Student对象并为它分配了一块内存,该内存地址为0x123,
- Student stu = Student();就是把创建的内存块的地址赋值给stu。
7.方法
7.1方法的格式
修饰符 返回值 方法名(参数列表)throw 抛出异常类型{
//方法体
}
7.2修饰符
修饰符有:public、protected、default(默认,不写出来)、private、static、final(前四个为访问控制修饰符),修饰符没有顺序之分。
public class Student{
//在学生类中定义一个study方法
public void study()throw Exception{
System.out.println("我在学习");
}
}
可以不写修饰符,不写时为默认,没有抛出异常时也可不写异常。剩下的都是一个方法的必备部分。
public class Student{
//在学生类中定义一个study方法
void study(){
System.out.println("我在学习");
}
}
7.3返回值
返回值类型和方法名都是必须写的,只有一种方法不用写返回值类型,那就是构造方法,返回的是本类。
当有返回值时,必须使用return返回一个值,不然编译报错。
public class Student{
//在学生类中定义一个study方法
String study(){
System.out.println("我在学习");
return "我在学习";//返回了一个String类型的值
}
}
那么,返回值类型为void的方法可不可以存在return关键字呢?
可以的,return有两个作用,一个是返回一个值,另一个是结束本方法。如:
public class Student{
//在学生类中定义一个study方法
void study(){
System.out.println("我在学习");
return;
}
}
7.4方法名规范
方法名符合标识符命名规范即可(可中文,但不推荐。起有意义的名字)
推荐使用规范:方法名第一个单词首字母小写其余单词首字母大写。如:sayHello()。
7.5方法的执行
上面都是方法定义,并没有被执行,方法需要被调用后才能够执行。如何去调用呢,是哪个类的方法就用哪个类的对象去调用(static方法同样可行,但不推荐)。
public class Student{
//定义study方法
public void study(){
System.out.println("我在学习!!");
}
//程序执行入口main方法
public static void main(String[] args){
//方法的执行首先创建该方法的本类对象也就是Student类对象
Student stu = new Student();
//再用该对象调用方法
stu.study();
}
}
7.6 方法的递归调用
什么是递归?
简单的说,递归就是自己调自己,在代码中表现为方法的内部又调用了该方法。如:
public void test(){
test();
}
如果只是这样,那会无限打开test方法形成一个死循环,编译不会报错,但代码运行表现为出现错误StackOverflowError,如下图:
这时,我们需要一个条件来退出这个递归序列,也就是执行到何时把方法关闭了,如:
int a = 1;
public void test(){
//...
a++;
if(a==10){
return;
}
test();
//...
}
通过以上两段代码,我们可以看出:递归由递归头和递归体组成
控制方法退出的条件为递归头,递归体就是自己调自己,调用本身这个方法。
例题:用递归计算1~100之和
public int f(int num){
if(num==1){
return 1;
}
return num+f(num-1);
}
7.7 方法参数
7.7.1 参数列表类型
- 无参
- 一参
- 多参
- 可变参
public class Student{
void study(){
System.out.println("这是一个无参方法。");
}
void study(String s){
System.out.println("这是一个一参方法。"+s);
//+ 在这里是一个连接符
}
void study(String s,String s){//多个参数间用逗号隔开
System.out.println("这是一个多参方法。");
}
void study(String... s){//可变参的本质是一个数组
System.out.println("这是一个可变参方法。");
}
}
7.7.2参数传递
参数类型:
形参(形式参数):用来接收值,本身是没有值的
实参(实际参数):就是一个值
public class Student{
public static void main(String[] args){
Student stu = new Student();
stu.study(1);//在这里1是一个实际的值,是一个 实参
}
public void study(int a){//这里的a没有特定的值,用来接收值给方法使用 ,所以这里是一个 形参
System.out.println("我在学习一个数,这个数是:"+a);
}
}
参数传递(把形参传给实参)的两种方式:
看的是传递的值的类型
值传递:对于基本数据类型来说参数的传递形式是把存储的值copy一份赋值给形参,也就是说在方法中改变这个值是不会对原来存储的值做出改变。
例子如下:
public class Test {
//该方法中,改变参数当前的值
public static void changeNum(int a) {
a = 10;
}
public static void main(String[] args) {
int a = 1;
System.out.println("before: a = " + a);//传参之前,变量a的值
changeNum(a);
System.out.println("after: a = " + a);//传参之后,变量a的值
}
}
引用传递:对于引用数据类型(在java中除了基本数据类型,剩下的就是引用数据类型)来说,参数传递传的是一个地址,传递之后,形参和实参指向的是同一片内存空间。也就是说形参和实参现在就像两个遥控器,可以控制同一个空调(地址)。
例子如下:
public class Test{
//该方法中,改变引用s所指向对象的name属性值
public static void changeName(Student s){
s.name = "tom";
}
public static void main(String[] args){
Student s = new Student();
System.out.println("before: name = "+s.name);//传参之前,引用s所指向对象的name属性值
changeName(s);
System.out.println("after: name = "+s.name);//传参之后,引用s所指向对象的name属性值
}
}
7.8方法的重载
重载方法必须在同一个类中
重载方法方法名必须与原方法方法名一致。
方法的重载只跟方法的参数列表有关,与方法的修饰符 、返回值类型、抛出的异常无关。
7.8.1方法重载情况
参数列表不同的情况有:
- 参数的个数不同
- 参数的顺序不同
- 参数的类型不同
例如:
public class Test{
//参数的个数不同
public void test(){}
public void test(int a){}
public void test(String s1,String s2){}
//参数的类型不同
public void test(boolean flag){}
public void test(double salary){}
//参数的顺序不同
public void test(int a,String s){}
public void test(String s,int a){}
//方法的参数可以相同,也可以不同
public int test(int a,int b){
return 0;
}
}
7.8.2不构成方法重载的情况:
public class Test{
//原方法
public void test(int a,int b){}
//方法不构成重载,编译报错,会提示方法已定义的
public void test(int b,int a){}
//返回值类型不同不构成重载
public int test(int a,int b){return 0;}
//修饰符不同不构成重载
private void test(int a,int b){}
}
7.8.3 方法重载的特殊情况:
public class Test{
//方法重载
public void test(int a,long b){}
public void test(long b,int a){}
public static void main(String[] args){
Test t = new Test();
t.test(1,1L);//调用到第一个方法
t.test(1L,1);//调用到第二个方法
t.test(1,1);//问题:这个会调用到哪一个test方法?
}
}
上述问题,编译报错
因为参数无法匹配方法时会尝试做类型转换,但是类型转换后两个方法同时匹配,所以编译报错。
public class Test {
//重载方法1
public void test(int a){}
//重载方法2
public void test(short a){}
public static void main(String[] args){
Test t = new Test();
byte a = 1;
t.test(a);//这里会调用第二个方法,也就是short类型参数的test方法
}
}
虽然byte类型数据,可以自动转换为short,也可以转换为int,但是short离byte“更近” 。