Java面向对象(上) (Obeject Oriented Programming)
面向对象的三条主线
- Java类及类的成员:属性、方法、构造器;代码块、内部类
- 面向对象的三大特征:封装、继承、多态
- 其它关键字:this,super,static,final,abstract,interface,package,import
1.类和对象的基本概念
- 类是抽象的,是概念上的定义
- 对象是类的实例(instance)
- 面向对象的程序设计的重点是类的设计
- 设计类,就是设计类的成员
属性 = 成员变量(field) = 域、字段
方法 = 成员方法(method)= 函数
2.类和对象的使用
- 创建类、设计类的成员(属性和方法)
- 创建类的对象
- 通过对象.属性或对象.方法调用对象的结构
//测试类
public class PersonTest{
public static void main(String[] args) {
//1.创建类的对象 = 类的实例化
Person man = new Person();
//2.调用对象的属性"对象名.属性"
man.name = "Tom";
man.age = 20;
man.isMale = true;
System.out.println(man.name+" "+man.age+" "+man.isMale);
//3.调用对象的方法"对象名.方法"
man.eat();
man.talk("English");
}
}
class Person{
//属性(成员变量)
String name;
int age;
boolean isMale;
//行为(方法)
public void eat(){
System.out.println("人可以吃饭");
}
public void talk(String language){
System.out.println("人的语言是"+language);
}
}
- 对象名(指针)存储在栈(stack)空间中
- 对象是存储在堆(heap)空间中,包括对象的成员变量
3.属性和局部变量
3.1 声明位置
属性:直接定义在类的{}内
局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内内部的变量
3.2 权限修饰符不同
属性:可以在声明属性时,指明其权限,使用权限修饰符:private,public,缺省,protected(封装性)
局部变量:没有修饰符
3.3 默认初始化值
属性:类的属性,根据其类型,都有默认的初始化值
整形(byte,short,int,long):0
浮点型(float,double):0.0
字符(char):0(或’\u0000’)
布尔类型(boolean):false
引用数据类型(数组、类、接口)null
局部变量:没有初始化赋值
3.4 内存中加载的位置
属性:加载到堆中(heap)(非static)
局部变量:加载到栈中(stack)
4.方法
方法的声明:权限修饰符+返回值类型+方法名(形参列表)
private,public,protected,缺省
public String whatNation(String nation){
String whatnation = "国籍是"+nation;
return whatnation;
}
方法的使用中,可以调用当前类中的属性或方法
4. 对象数组
定义了一个Student类
class Student{
int number; //学号
int state; //年级
int score; //成绩
public String info(){
return "学号 "+number+" 年级"+state+" 成绩 "+score;
}
}
下面在StudentTest类中创建对象数组
public class StudentTest{
public static void main(String[] args){
Student[] stus = new Student[20];
for(int i = 0;i<stus.length;++i){
//给对象数组中元素赋值,创建一个对象,把指针赋给它
stus[i] = new Student();
//给Student类型的对象赋值,即给对象的属性赋值
stus[i].number = i+1;
}
}
}
Students[] stus = new Student[20];
这条语句执行完,栈中有stus,stus的值是对象数组的地址,
对象数组存储在堆中,当前对象数组的每个元素stus[i] = null
stu[i] = new Students();
执行这条语句后,数组元素变为对象的地址,对象是在堆中的另一片存储空间中
注意:类中的属性如果是引用类型变量,记得new创建对象,否则就是空指针,有2种方法
- 定义属性的时候new
- 在构造器中new
5.万事万物皆对象的简单理解
- 在Java语言范畴中,我们将功能、结构封装到类中,通过类的实例化,来调用具体的功能结构
- Java和前段HTML、后端数据交互时,前后端的结构在Java层面交互时,都体现为类、对象
- 例如数据库中表格中的一条记录可以视为一个对象
6.方法的几个知识点
6.1 方法的重载(overload)
在同一个类中,允许存在一个以上的同名方法,只要参数的个数或类型不同即可
- 两同、一不同:同类,同名,参数列表不同
- 例如Arrays中重载的sort()/binarysearch(),针对不同数据类型
- 和返回类型、形参名、修饰符、方法体无关
public class OverloadTest{
//两个方法同名,在同一个类中,但是参数列表不同
public int getMax(int i,int j){
}
public double getMax(double i,double j){
}
}
6.2可变形参个数的方法
- 格式:例如public void show(int number1, double number2, String … str),String可以替换成其他的数据类型
在使用方法的时候可以输入可变个数的字符串,但必须都是字符串(…前面指定的数据类型) - 可变个数形参只能在形参列表的末尾,且只能有1个
//可变个数形参的方法
public class MethodArgsTest{
public static void main(String[] args) {
MethodArgs test = new MethodArgs();
test.show("hello","world","frank");
test.show(new String[]{"hello","world","frank"});
}
}
class MethodArgs{
void show(String ... str){ //用String... 表示可变个数形参,str可以理解成字符串数组的首地址,访问也是照常访问
System.out.println("使用可变个数形参的方法");
for(int i = 0;i<str.length;++i){
System.out.println(str[i]);
}
}
void showArray(String[] str){ //这两个方法不能重名,即不能重载,两者本质上是等价的
System.out.println("使用数组来传递参数");
for(int i= 0;i<str.length;++i){
System.out.println(str[i]);
}
}
}
6.3 值传递机制(传值和传地址)
- 对于基本数据类型,方法是传值
- 对于引用数据类型,方法是传地址(毕竟引用数据类型就是指针)
想在原来的数据上做修改,就必须和地址(指针、引用)相关
- 注意:Java和C不一样的是没有显式指针了(这保证了Java的安全性)
如果在main函数中定义了int a, 是无法获取a的地址的
下面是交换功能的两种情况,体会一下
public class ValueTransferTest02 {
public static void main(String[] args) {
Data data = new Data();
data.a = 10;
data.b = 30;
data.swap(data); //传递引用数据类型
System.out.println("a = "+data.a+" b = "+data.b);
}
}
class Data{
int a;
int b;
void swap(Data data){
int temp;
temp = data.a;
data.a = data.b;
data.b = temp;
}
}
//方法的参数传递机制、
//传值还是传地址
public class ValueTransferTest {
public static void main(String[] args) {
int [] array = new int[]{2,4,1,3,5,-1};
System.out.println("排序前");
for(int i = 0;i<array.length;++i){
System.out.print(array[i]+" ");
}
System.out.println("\n排序后");
new Array().selectSort(array);
for(int i = 0;i<array.length;++i){
System.out.print(array[i]+" ");
}
}
}
class Array{
void selectSort(int []array){
for(int i = 0;i<array.length;++i){
int k = i;
for(int j = i+1;j<array.length;++j){
if(array[j]<array[k]){
k = j;
}
}
if(k!=i){
swap(array,k,i);
}
}
}
//要实现交换的功能,需要传递地址,也就是这里的array
//如果不是数组的话,就要传递对象
//总之实现交换,需要地址
void swap(int[] array,int i,int j){
int temp;
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
7. 封装性
高内聚,低耦合
- 高内聚:类的内部数据操作细节自己完成,不允许外部干涉
- 低耦合:仅对外暴露少量的方法用于使用
7.1问题的引入
当我们创建一个类的对象创建一个对象后,可以通过"对象.属性"的方式,对属性进行赋值。这样的赋值只受到数据类型的限制,在实际情况中很有可能是非法的。因此我们需要将属性的权限改为private,这样只能通过方法对属性进行修改
- 属性的隐藏(封装性)
7.2封装性的体现
- 将类的属性私有化(private),提供公共的(public)方法来设置(setLegs)和获取(getLegs)属性
- 不对外暴露的私有方法
- 单例模式
…
class Animal{
private legs;
//设置属性
public void setLegs(int legNumber){ //set方法有形参,没有返回值
if(legNumber<0){
legs = 0;
}else{
legs = legNumber;
}
}
//获取属性
public int getLegs(){ //get方法没有形参,有返回值
return legs;
}
}
setXxx()和getXxx()要熟练掌握
7.3权限修饰符
- 4种权限修饰符(从小到大):privte,缺省,protected,public
修饰符 类内部 同一个包 不同包的子类 同一个工程 private Yes 缺省 Yes Yes protected Yes Yes Yes public Yes Yes Yes Yes
- 4种权限可以用来修饰 类、类的内部结构:属性、方法、构造器、内部类
- 用来修饰类只能用public或者缺省
简单总结:Java的4种权限修饰来修饰类和类的内部结构,体现类和类的内部结构的可见性的大小
8.构造器(constructor)
类的结构之三:构造器(或构造方法)
8.1 构造器的作用
- 创建类的对象:new + 构造器
- 如果没有定义构造器,系统默认提供一个空参的构造器
Person p = new Person(); //默认提供的空参构造器
- 初始化对象的属性
Person p = new Person("Tom"); //这句在测试类的main方法中
class Person {
String name;
int age;
//构造器格式 = 修饰符+类名(形参列表){}
public Person() {
System.out.println("你调用了自定义的构造器");
}
public Person(String n){
System.out.println("你调用了自定义的构造器,并且初始化了对象的属性");
name = n;
}
}
8.2 构造器的格式
修饰符+类名(形参列表){}
public Person(String name){
//操作
}
- 一个类中的多个构造器构成重载(同名、同类、形参列表不同)
- 一旦我们显式的定义了一个类的构造器之后,系统就不再提供默认的空参构造器
- 定义一个类一般都是写的顺序是:属性、构造器、方法
- 默认构造器的权限和类的权限是相同的
9.JavaBean
JavaBean是一种Java语言写成的可重用组件
所谓JavaBean是符合以下条件的类
- 类是公共的(public)
- 有一个无参的公共的构造器
- 有属性,且有对应的set,get方法
JavaBean在后续数据库等的学习中会发挥作用
10.UML类图
要求会看UML类图
11.关键字 this
this可以理解成当前对象或当前正在创建的对象(构造器中的就是正在创建的对象)
- this 可以用来修饰、调用:属性、方法、构造器
- 可以用"this.属性"和"this.方法"来调用当前对象属性或方法,但是通常省略this
- 特殊的,如果(方法中或构造器中)形参和属性重名了,需要使用 “this.属性”
//this修饰属性
class Person{
private String name;
public Person(String name){
this.name = name; //this修饰属性
}
public void eat(){
System.out.println("吃饭");
}
}
- 在一个构造器中使用this调用其他构造器———— this.Person(形参列表)
- 规定:使用this(形参列表)调用其他的构造器,必须在当前构造器的首行
//this调用构造器
class Person{
private String name;
public Person(){
this.eat();
//Person初始化需要40行代码
}
public Person(String name){
this(); //使用this调用构造器
this.name = name;
}
public Person(String name,int age){
this(age); //使用this调用构造器
this.name = name;
}
public void eat(){
System.out.println("吃饭");
}
}
12.package 关键字
package 包
- 方便项目中类的管理,提供包的概念
- 使用package声明类或接口所属的类,声明在源文件的首行,表明当前源文件所属的包,例如:package com.atguigu.exer1
- 包,属于标识符,遵循标识符的命名规则,全部小写,见名知意
- 每"."一次就代表一层文件目录
- 补充:
- 同一个包下不能定义同名的类、接口
- 不同的包下能定义同名的类、接口
JDK中主要的包
13.MVC设计模式
View 视图 Controller 控制器 Module 模型
14. import 关键字
import:导入
- 在源文件中显式地导入指定包下的类和接口
import java.util.*;
- 声明在包的声明和类的声明之间
- 如果使用的类或接口时java.lang包下定义的,那么可以省略import,java.lang的意思是language,是Java的核心包。例如String、System就是在这个包下
- 如果类或者接口在同一个包下,也不需要import
- 如果在源文件中使用了不同包下同名的类,那么必须至少有一个类用全类名的方式显示
//假设这个类在当前包下
Account acc0 = new Account();
//全类名的方式显示
com.atguigu.exer3.Account acc = new com.atguigu.exer3.Account();
- 使用"xxx.*"表明可以调用xxx包下的所有结构。但是如果使用的是xxx子包下的结构,则仍然需要显式地导入
- “import static +package” 导入指定类或接口中的静态结构:属性或方法
- 注意这里import的落脚点是属性或方法,而没有static的import的落脚点是类或接口
import static java.lang.System.*;
//main方法中
//out是静态的(static)
out.println("Hello");