文章目录
一、类
面向对象的语言的特点:封装性,继承和多态
类:一种用于创建具体实例(对象)的数据类型
1.类的定义
- 类的定义包括两部分,及类声明和类体,基本格式是
class 类名{
类体的内容
}
//class 类名 是类的声明部分,花括号里面是类体
-
抽象的关键是抓住事物的两个方面——属性和行为,即数据以及在数据上进行的操作。
因此,类体的内容包括两部分:
1)变量的声明(用来存储属性的值,体现对象的属性)
2)方法的定义(方法可以对类中声明的变量进行操作,即给出算法,体现了对象的行为)
2.成员变量
-
声明变量部分所声明的变量称为成员变量或域变量
-
从概念或叫法上看:成员变量=属性= field(即成员变量是用来表示属性的)
-
注意事项和细节说明
1)属性的定义语法同变量,示例:访问修饰符 属性类型 属性名
访问修饰符:控制属性的访问范围,如public,protected,默认,private
2)属性的定义类型可以为任意类型,包含基本类型或引用类型
3)属性如果不赋值,有默认值,规则和数组一致。
4)成员变量在整个类的所有方法里都有效,其有效性与它在类体中出现的位置无关。
5)如何访问属性
基本语法:对象名.属性名
3.成员方法
-
基本介绍
在某些情况下,我们要需要定义成员方法(简称方法)。比如人类:除了有一些属性外(年龄,姓名…),我们人类还有一些行为比如:可以说话、跑步…,通过学习,还可以做算术题。这时就要用成员方法才能完成。现在要求对Person类完善。 -
成员方法快速入门
1)添加speak成员方法,输出我是一只好人2)添加cal01成员方法,可以计算从1+…+1000的结果
3)添加cal02成员方法,该方法可以接收一个数n,计算从1+…+n的结果
4)添加getSum成员方法,可以计算两个数的和
public class hello{ public static void main(String[] args){ //方法使用 //1.方法写好以后如果不去调用,不会输出 Person p1 = new Person(); p1.speak();//调用方法 p1.cal01();//调用cal01方法 p1.cal02(5);//调用cal02方法,同时给n=5; //调用getSum方法,同时n1=10,n2=20; //把方法getSum返回的值,赋给变量r int r = p1.getSum(10,20); System.out.println("getSum返回值=" + r); } } class Person { String name; int age; //方法(成员方法) //添加speak成员方法,输出“我是一个好人” //1.public 表示方法是公开的 //2.void 表示方法没有返回值 //3.speak() 表示speak是方法名,()形参列表 //4.{}是方法体,可以写我们要执行的代码 //5.System.out.println("我是一个好人");表示我们的方法是输出一句话 public void speak() { System.out.println("我是一个好人"); } public void cal01() { int res = 0; for(int i = 1; i <= 1000; i++){ res+=i; } System.out.println("01计算结果=" + res); } public void cal02(int n) { int res = 0; for(int i = 1; i <= n; i++){ res+=i; } System.out.println("02计算结果=" + res); } //(int n1,int n2) 形参列表,2个形参,可以接收用户传入两个数 public int getSum(int n1,int n2) { int res = n1 + n2; return res; } }
方法的调用机制原理:
-
成员方法的好处
提高代码的复用性
可以将实现的细节封装起来,然后供其他用户来调用即可。
-
成员方法的定义
访问修饰符 返回数据类型 方法名(形参列表…){//方法体
语句;
return 返回值;}
-
形参列表:表示成员方法输入 cal(int n),getSum(int n1, int n2)
-
返回数据类型:表示成员方法输出,void表示没有返回值
-
方法主体:表示为了实现某一功能代码块
-
return语句不是必须的。
-
-
注意事项和使用细节
-
访问修饰符(作用是控制方法使用的范围)
如果不写默认访问,[有四种: public,protected,默认, private],具体在后面说
-
返回数据类型
1)一个方法最多有一个返回值[返回数组可以返回多个结果 ]
2)返回类型可以为任意类型,包含基本类型或引用类型(数组,对象)
3)如果方法要求有返回数据类型,则方法体中最后的执行语句必须为return值;而且要求返回值类型必须和return的值类型一致或兼容
4)如果方法是void,则方法体中可以没有return语句,或者只写 return ;
-
方法名
遵循驼峰命名法,最好见名知义,表达出该功能的意思即可,比如得到两个数的和getSum,开发中按照规范 -
形参列表
1)一个方法可以有0个参数,也可以有多个参数,中间用逗号隔开,比如getSum(int n1,int n2)2)参数类型可以为任意类型,包含基本类型或引用类型,比如printArr(int[] [] map)
3)调用带参数的方法时,一定对应着参数列表传入相同类型或兼容类型的参数
4)方法定义时的参数称为形式参数,简称形参;方法调用时的传入参数称为实际参数,简称实参,实参和形参的类型要一致或兼容、个数、顺序必须一致!
-
方法体
里面写完成功能的具体的语句,可以为输入、输出、变量、运算、分支、循环、方法调用,但里面不能再定义方法!即:方法不能嵌套定义。 -
方法调用细节说明
1)同一个类中的方法调用:直接调用即可。比如print(参数);2)跨类中的方法A类调用B类方法:需要通过对象名调用。比如对象名.方法名(参数);
3)特别说明一下:跨类的方法调用和方法的访问修饰符相关,后面细说。
-
二、构造方法与对象的创建
1.构造方法
-
基本语法
[修饰符] 方法名(形参列表){
方法体;
}1)构造方法的修饰符可以默认
2)构造方法没有返回值
3)方法名和类名字必须一样
4)参数列表和成员方法一样的规则
5)构造方法的调用,由系统完成
-
基本介绍
构造方法又叫构造器,是类的一种特殊的方法,它的主要作用是完成对新对象的初始化。它有几个特点:
1)方法名和类名相同
2)没有返回值
3)在创建对象时,系统会自动的调用该类的构造器完成对对象的初始化。
-
注意事项和使用细节
1.一个类可以定义多个不同的构造方法,即构造方法重载
2.构造方法没有返回值
3.构造方法是完成对象的初始化,并不是创建对象
4.在创建对象时,系统自动的调用该类的构造方法
5.如果没有定义构造方法,系统会自动给类生成一个默认无参构造方法(也叫默认构造方法)
6.一旦定义了自己的构造方法,默认的构造方法就覆盖了,就不能再使用默认的无参构造方法,除非显式的定义一下,即:Person(){}7.构造方法也可以重载
2.创建对象
-
先声明再创建
Cat cat ;//声明对象 cat cat = new Cat(0);//创建
-
直接创建
Cat cat = new Cat(0);
3.类和对象的内存分配机制
1)Java内存的结构分析
1.栈:一般存放基本数据类型(局部变量)
2.堆:存放对象(Cat cat,数组等)
3.方法区:常量池(常量,比如字符串),类加载信息
4.示意图[Cat (name, age, price)]
2)对象的内存模型
1.声明对象时的内存模型
在声明对象后,进行的是默认初始化,这时的该对象是一个空对象。
2.为对象分配变量后的内存模型
如系统在见到下方句子时
Cat cat = new Cat();
会为该对象的各个变量分配内存,即Cat类的成员变量被分配内存空间,然后执行构造方法中的语句。之后将计算出一个称作引用的值(可以说是地址,该值包含着代表这些成员变量的内存位置及相关的重要信息),然后赋给cat,之后这些内存单元将有cat操作管理
ps:一个类通过使用new运算符可以创建多个不同对象,这些对象的变量将被分配不同的内存空间,并各自返回引用给相应的对象。
-
举例
Person p1 = new Person(); p1.age = 10; p1.name = "小明” ; Person p2=p1; System.out.println(p2.age);
1)先加载Person类信息(属性和方法信息,只会加载一次)
2)在堆中分配空间,进行默认初始化(看规则)
3)把地址赋给p,p就指向对象
4)进行指定初始化,比如p1.age =10,p1.name =" 小明"
4.补充
-
避免使用空对象,java编译器对空对象不作检查,程序中如果使用了空对象运行时会出现异常NullPointerException。
-
与C++不同,java没有析构方法,但是有垃圾收集机制
垃圾收集机制:一个类声明的两个对象如果具有相同的引用,那么二者就具有完全相同的实体,Java的垃圾收集机制会周期地检测某个实体是否已不再被任何对象所拥有(引用),如果发现这样的实体,就释放实体占有的内存。
如果希望java虚拟机立刻进行垃圾收集操作,可以让System类调用gc()方法。
-
在使用System.out.println(cat)输出对象cat中存放的引用值时,java会进行一些处理,比如给引用值添加前缀信息类名@,然后输出添加了前缀信息的数据。可以让System类调用静态方法int identityHashCode(Cat cat)返回对象cat的引用。
int address = System.identityHashCode(cat);
三、类与程序的基本结构
-
一个Java应用程序(也称为一个工程),由若干个类构成,这些类可以在一个源文件中,也可以分布在若干个源文件
-
Java应用程序有一个主类,即含有main()方法的类,Java应用程序从主类的main()方法开始执行。在编写一个Java应用程序时,可以编写若干个Java源文件,每个源文件在编译后产生若干个类的字节码文件,因此经常需要进行如下的操作:
(1)将应用程序涉及的Java源文件保存在相同的目录中,分别编译通过,得到Java应用程序所需要的字节码文件。
(2)运行主类
-
当使用解释器运行一个Java应用程序时,Java 虚拟机将Java应用程序需要的字节码文件加载到内存,然后由Java 的虚拟机解释执行。因此可以事先单独编译一个Java应用程序所需要的其他源文件,并将得到的字节码文件和主类的字节码文件存放在同一目录中。如果应用程序的主类的源文件和其他的源文件在同一目录中,也可以只编译主类的源文件,Java系统会自动地先编译主类需要的其他源文件。
四、成员方法传参机制
-
基本数据类型的传参机制
基本数据类型,传递的是值(值拷贝),形参的任何改变不影响实参
对于基本数据类型的参数,向该参数传递的值的级别不可以高于该参数的级别·,例如不能向一个int型参数传递一个float值,但是可以向double型参数传递一个float值。
-
引用数据类型(数组,对象,接口)的传参机制
引用类型传递的是地址(传递也是值,但是值是地址),可以通过形参影响实参!
-
方法递归调用
-
介绍
递归就是方法自己调用自己,每次调用时传入不同变量
-
递归重要规则
1)执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
2)方法的局部变量是独立的,不会相互影响,比如n变量
3)如果方法中使用的是引用类型变量(比如数组),就会共享该引用类型的数据.
4)递归必须向退出递归的条件逼近,否则就是无限递归,
5)当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕。
-
五、可变参数
-
基本概念
java允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法。
-
基本语法
访问修饰符 返回类型 方法名(数据类型…形参名){
}
public void f(int ...x) //称x是可变参数的参数代表
-
注意事项和使用细节
1)可变参数的实参可以为0个或任意多个。
2)可变参数的实参可以为数组。
3)可变参数的本质就是数组.参数代表可以通过下标运算来表示参数表列中的具体参数,即x[0],x[1],…,x[n]
4)可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
5)一个形参列表中只能出现一个可变参数
6)对于可变参数,Java提供了增强的for语句
//for(声明循环变量:参数代表){ //…… //} for(int p:x){ sum = sum + p; }
六、对象的组合
- 一个类可以把某个对象作为自己的一个成员变量,也就是该类的对象将其他对象作为自己的组成部分,即Has-A。
- 如果一个对象a组合了对象b,那么对象a就可以委托对象b调用其方法,即对象a以组合的方式复用对象b的方法。
- 通过组合对象来复用方法有以下特点:
(1)通过组合对象来复用方法也称“黑盒”复用,因为当前对象只能委托所包含的对象调用其方法,这样当前对象对所包含对象的方法的细节(算法的细节)是一无所知的。
(2)当前对象随时可以更换所包含的对象,即对象与所包含的对象属于弱耦合关系。 - 举例
public class Circle {
double radius,area;
void setRadius(double r) {
radius=r;
}
double getRadius() {
return radius;
}
double getArea(){
area=3.14*radius*radius;
return area;
}
}
public class Circular {
Circle bottom;
double height;
void setBottom(Circle c) {
bottom = c;
}
void setHeight(double h) {
height = h;
}
double getVolme() {
if(bottom == null)
return -1;
else
return bottom.getArea()*height/3.0;
}
double getBottomRadius() {
return bottom.getRadius();
}
public void setBottomRadius(double r){
bottom.setRadius(r);
}
}
public class Example{
public static void main(String args[]) {
Circle circle = new Circle();
circle.setRadius(10);
Circular circular = new Circular();
System.out.println("circle的引用:"+circle);
System.out.println("圆锥的bottom的引用:"+circular.bottom);
circular.setHeight(5);
circular.setBottom(circle);
System.out.println("circle的引用:"+circle);
System.out.println("圆锥的bottom的引用:"+circular.bottom);
System.out.println("圆锥的体积:"+circular.getVolme());
System.out.println("修改circle的半径,bottom的半径同样变化");
circle.setRadius(20);
System.out.println("bottom的半径:"+circular.getBottomRadius());
System.out.println("重新创建circle,cirlce的引用将发生变化");
circle = new Circle();
System.out.println("circle的引用:"+circle);
System.out.println("但是不影响circular的bottom的引用");
System.out.println("圆锥的bottom的引用:"+circular.bottom);
}
}
七、成员变量与局部变量
1.成员变量与局部变量
- 基本使用
-
在java编程中,主要的变量就是属性(又称成员变量或全局变量)和局部变量。
-
我们说的局部变量一般是指在成员方法中定义的变量。
-
java中作用域的分类
全局变量:也就是属性,作用域为整个类体
局部变量:也就是除了属性之外的其他变量,作用域为定义它的代码块中
-
全局变量有默认值,但局部变量没有默认值,因此在使用局部变量之前,必须保证局部变量有具体的值。对全局变量的操作只能放在方法中,方法可以使用各种语句对全局变量和方法体中声明的局部变量进行操作。但是在声明全局变量时可赋予初值。
-
注意事项和细节使用
-
如果局部变量与全局变量同名,全局变量会被隐藏,即全局变量在这个方法中暂时失效,如果想在该方法中使用被隐藏的全局变量,必须使用关键字this(后面细说)
-
在同一个作用域中,比如在同一个成员方法中,两个局部变量,不能重名。
-
属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁。局部变
量,生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而销毁即在一次方法调用过程中。 -
作用域不同
全局变量:可以被本类使用,或其他类使用(通过对象调用)
局部变量:只能在本类中对应的方法中使用
-
修饰符不同
全局变量/属性可以加修饰符
局部变量不可以加修饰符
-
-
var局部变量
用var声明局部变量,编译器可以推断出该变量的类型。
var仅限于在方法体内声明局部变量,方法的参数和方法的返回类型不可以用var来声明,并且必须显式地指定初值(初值不可以是NULL)
2.实例成员与类成员
-
成员变量(全局变量)又可分为实例变量和类变量
在声明成员变量时,用关键字static置于变量的类型的前面修饰的称作类变量(static变量,静态变量),否则称作实例变量。
不能用static修饰局部变量。 -
实例变量与类变量的区别
1)不同对象的实例变量互不相同
2)所有对象共享类变量
3)可以通过类名直接访问类变量
ps:
类变量实际上没有破坏封装性
当Java程序执行时,类的字节码文件被加载到内存,如果该类没有创建对象,类中的实例变量不会被分配内存。但是类中的类变量,在该类被加载到内存时就被分配了相应的内存空间
3.实例方法和类方法
-
类中的方法又可分为实例方法和类方法
在声明方法时,用关键字static置于方法类型的前面修饰的称作类方法(static方法,静态方法),否则称作实例方法。
不能用static修饰构造方法
-
实例方法与类方法的区别
1)当类的字节码文件被加载到内存时,类的实例方法不会被分配人口地址,只有当该类创建对象后,类中的实例方法才分配入口地址,从而实例方法可以被类创建的任何对象调用、执行.需要注意的是,当创建第一个对象时,类中的实例方法就分配了入口地址,当再创建对象时,不再为实例方法分配入口地址。也就是说,方法的入口地址被所有的对象共享,当所有的对象都不存在时,方法的入口地址才被取消。
在实例方法中不仅可以操作实例变量,也可以操作类变量,实例方法可以调用类中的实例方法和类方法(不包括构造方法)。当对象调用实例方法时,该方法中出现的实例变量就是分配给该对象的实例变量,该方法中出现的类变量也是分配给该对象的变量,只不过这个变量和所有的其他对象共享而已。
2)类名调用类方法
对于类中的类方法,在该类被加载到内存时就分配了相应的人口地址,从而类方法不仅可以被类创建的任何对象调用、执行,也可以直接通过类名调用。类方法的人口地址直到程序退出才被取消。需要注意的是,实例方法不能通过类名调用,只能由对象来调用。
和实例方法不同的是,类方法不可以操作实例变量,因为在创建对象之前实例成员变量还没有分配内存。类方法不可以调用类中的实例方法,只可以调用类中的类方法
如果一个方法不需要操作类中的任何实例变量或调用类中的实例方法就可以满足程序的需要,则可以将这样的方法设计为一个static方法。
八、重载(overload)
-
基本介绍
java中允许同一个类中,多个同名方法的存在,但要求形参列表不—致!
(参数的个数或者参数列表中对应的某个参数的类型不同)
-
重载的好处
1)减轻了起名的麻烦
2)减轻了记名的麻烦
-
注意事项和使用细节
1)方法名∶必须相同
2)形参列表:必须不同(形参类型或个数或顺序,至少有一样不同,参数名无要求)
3)返回类型:无要求
九、this
-
什么是this
java虚拟机会给每个对象分配this,代表当前对象。
简单地说,哪个对象调用,this就代表哪个对象
-
this的注意事项和使用细节
- this关键字可以用来访问本类的属性、方法、构造方法
- this用于区分当前类的属性和局部变量
- 访问成员方法的语法:this.方法名(参数列表);
- 访问构造方法语法:this(参数列表);注意只能在构造方法中使用,比如构造方法重载的时候
- this不能在类定义的外部使用,只能在类定义的方法中使用。
- this不能出现在类方法中
十、包
-
包的三大作用
- 区分相同名字的类
- 当类很多时,可以很好的管理类[看Java API文档]
- 控制访问范围
-
包的基本语法
package com.hspedu;
说明:
- package关键字,表示打包.
- com.hspedu:表示包名
-
包的本质分析(原理)
包的本质实际上就是创建不同的文件夹来保存类文件,画出示意图。
-
包的命名
-
命名规则:
只能包含数字、字母、下划线、小圆点…但不能用数字开头,不能是关键字或保留字
demo.class.exec11 //错误class是关键字 demo.12a //错误12a是数字开头 demo.ab12.oa //对
-
命名规范
一般是小写字母+小圆点一般是
com.公司名.项目名.业务模块名
比如:com.hspedu.oa.model; com.hspedu.oa.controller;
举例:
com.sina.crm.user //用户模块 com.sina.crm.order //订单模块 com.sina.crm.utils //工具类
-
-
有包名的类的存储目录
如果程序使用了包语句,如
package tom.jia
那么存储目录中必须包含……\tom\jia
可以将文件保存在该目录下,然后去其上一层目录进行编译
- 运行有包名的主类
运行时必须用主类的全名,即”包名.主类名“
也可以最里面的目录中使用通配符*,编译所有源文件
javac *.java
十一、import语句
-
常用的包
一个包下,包含很多的类,java中常用的包有:
java.lang //lang包是基本包,包含所有的基本语言类,默认引入,不需要再引入. javax.swing //包含抽象窗口工具集中的图形、文本、窗口GUI类 java.io //包含所有的输入输出类 java.util //util 包,系统提供的工具包,工具类,使用 Scanner java.net //包含所有实现网络功能的类 java.sql //包含操作数据库的类 java.awt //java的界面开发,GUI
-
引入类库中的类
语法: import 包;我们引入一个包的主要目的是要使用该包下的类
import java.util.Scanner; //只是引入一个类Scanner。
import java.util.*;//表示将java.util包所有都引入
如果没用import语句引入包中的类,那么,也可以直接带着包名使用该类的
-
引入自定义包中的类
1.有包名的源文件
包名路径左对齐,即源文件中的包名所对应的路径和它要用import语句引入的非类库的类的包名所对应的父目录相同
2.无包名的源文件
包名路径和源文件左对齐,即让源文件中import语句要引入的非类库的类的包名路径的父目录和用户源文件所在的目录相同 -
注意事项和使用细节
- package的作用是声明当前类所在的包,需要放在class的最上面,一个类中最多只有一句package
- import指令位置放在package的下面,在类定义前面,可以有多句且没有顺序要求。
十二、访问权限
-
基本介绍
java提供四种访问控制修饰符号控制方法和属性(成员变量)的访问权限(范围)∶
-
公开级别:用public修饰,对外公开
-
受保护级别:用protected修饰,对子类和同一个包中的类公开
-
默认(友好)级别:没有修饰符号.向同一个包的类公开
-
私有级别:用private修饰,只有类本身可以访问,不对外公开.
-
-
4种访问修饰符的访问范围
访问级别 访问控制修饰符 同类 同包 子类 不同包 公开 public √ √ √ √ 受保护 protected √ √ √ X 默认 无 √ √ X X 私有 private √ X X X -
使用的注意事项
1)修饰符可以用来修饰类中的属性,成员方法以及类
2)只有默认的和public才能修饰类!,并且遵循上述访问权限的特点。
3)在子类中的访问权限后续讲解
4)成员方法的访问规则和属性完全一样.
十三、OOP三大特征之封装
-
封装介绍
封装就是把抽象出的数据[属性]和对数据的操作[方法]封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作[方法],才能对数据进行操作。
-
封装的理解和好处
1)隐藏实现细节
2)可以对数据进行验证,保证安全合理
-
基本类型的类封装
不用构造方法创建基本类型类的对象,而是直接将一个基本数据类型赋值给所创建对象
Integer n1=100;
Double n2=6.2;
Float n3=3.14F;
数据类型要一致
可以对这样的对象进行四则运算
Byte、 Short、 Character、Integer、Long、Float和 Double对象分别调用byteValue()、shortValue() 、charValue(入、intValue() 、 longValue()、 floatValue()和 doubleValue()方法返回该对象含有的基本类型数据。
Character类还包括一些类方法,这些方法可以直接通过类名调用,用来进行字符的分类。
-
封装的实现步骤
1)将属性进行私有化【不能直接修改属性】2)提供一个公共的set方法,用于对属性判断并赋值
public void setXxx(类型 参数名){//Xxx表示某个属性
//加入数据验证的业务逻辑
属性=参数名;
}3)提供一个公共的get方法,用于获取属性的值
public 数据类型 getXxx(){//权限判断,Xxx表示某个属性
return xx;
} -
将构造器和setXxx结合
十四、对象数组
对象数组,即数组的元素是对象。
Student [] stu;
stu =new Student[10];
需要注意的是,上述代码仅定义了数组 Stu 中有10个元素,并且每个元素都是一个Student类型的对象,但这些对象目前都是空对象,因此在使用数组 stu中的对象之前应当创建数组所包含的对象。例如:
stu[0] = new Student();
- 举例
class Student{
int number;
}
public class Example {
public static void main(String args[ ]) {
Student stu[] = new Student[10];
for(int i=0;i<stu.length;i++) {
stu[i]=new Student();
stu[i].number = 101+i;
}
for(int i=0;i<stu.length;i++) {
System.out.println(stu[i].number);
}
}
}