类是面向对象中的一个很重要的概念,因为类是很多个具有相同属性和行为特征的对象所抽象出来的,对象是类的一个实例。
类具有三个特性:封装、继承和多态。
三大特征:
-
封装:核心思想就是“隐藏细节”、“数据安全”,将对象不需要让外界访问的成员变量和方法私有化,只提供符合开发者意愿的公有方法来访问这些数据和逻辑,保证了数据的安全和程序的稳定。所有的内容对外部不可见。
-
继承:子类可以继承父类的属性和方法,并对其进行拓展。将其他的功能继承下来继续发展 。
-
多态:同一种类型的对象执行同一个方法时可以表现出不同的行为特征。通过继承的上下转型、接口的回调以及方法的重写和重载可以实现多态。方法的重载本身就是一个多态性的体现。
三大思想:
面向对象思想从概念上讲分为以下三种:OOA、OOD、OOP
- OOA:面向对象分析(Object Oriented Analysis)
- OOD:面向对象设计(Object Oriented Design)
- OOP:面向对象程序(Object Oriented Programming )
类与对象:
(1)类是一类事务,是根据一般性抽象出来的;例如:人;动物.
-
类由属性和方法组成:
-
属性:就相当于人的一个个的特征
-
方法:就相当于人的一个个的行为,例如:说话、吃饭、唱歌、睡觉
-
类名称 对象名称 = new 类名称() ;
-
访问类中的属性或方法(方法的定义),则可以依靠以下的语法形式:
- 访问类中的属性: 对象.属性 ;
-
调用类中的方法: 对象.方法(实际参数列表) ;
- 类必须编写在.java文件中;
- 一个.java文件中,可以存在N个类,但是只能存在一个public修饰的类;
- .java文件的文件名必须与public修饰的类名完全一直;
- 同一个包中不能有重名的类;
(2)对象,是类的一个特例、实例。例如:小轿车、卡车、公共汽车等。对象通过new关键字,完成实例化(例如: Student stu = new Student() ; 。
(3)new关键字,其实本质就是申请分配内存,使Student类中的变量、方法都对应分配的内存单元。
面向过程和面向对象的区别:
面向过程 | 面向对象 | |
---|---|---|
区别 | 事物比较简单,可以用线性的思维去解决 | 事物比较负载,使用简单的线性思维无法解决 |
共同点 | 使用面向对象的思维 | 使用,面向过程来处理 |
优点: | 性能高,流程和步骤清晰,容易理解 | 易扩展、代码复用率高、利于后期扩展和维护,提升开发效率 |
不足: | 复用率低,扩展性差,维护难度高 | 一定程度上增加系统开销 |
面向对象程序设计的基本步骤:
- 发现类
- 发现类的属性
- 发现类的方法
类图:
类图以反映类的结构(属性,操作)及类之间的关系为主要目的,描述软件系统的结构,是一种静态建模的方法
类图包含如下三个组成部分:
- 定义的类名,如AppTree,Corn
- 类中属性,如AppleTree类中的name,brand等
- 类中方法,如print()方法
注意:”+“表示public ,”-“表示private,”#”表示protected
小结:
设计类是需要遵循的原则如下:
- 属性和方法是为了解决业务问题设置
- 关注主要属性和方法
- 如果没有必要,就不要增加额外的类,属性和方法
匿名对象:
- 没有对象名称的对象就是匿名对象。 即栈内存中没有名字,而堆内存中有对象。
- 匿名对象只能使用一次,因为没有任何的对象引用,所以将称为垃圾,等待被GC回收。
- 只使用一次的对象可以通过匿名对象的方式完成,这一点在以后的开发中将经常使用到。
public static void main(String[] args){
//Math2 m=new Math2();
//int num=m.sum(100,200);
//不通过创建对象名,直接实例对象调用,这就是匿名对象。因为没有对象名指向对象,所以只能调用一次,然后被GC回收。
int num = new Math().sum(100,200);
System.out.println(num);
}
class Math2{
int sum(int x,int y){
return x+y;
}
}
构造方法(构造器)
-
构造方法的作用?
用于对象初始化
-
语法:Person p = new Person();
在右侧Person后面出现的小括号, 其实就是在调用构造方法
语法:
[访问修饰符]方法名([参数列表]){
//…省略方法体的代码
}
-
执行时机:
在创建对象时,自动调用
-
特点
-
所有的Java类中都会至少存在一个构造方法
-
如果一个类中没有明确的编写构造方法, 则编译器会自动生成一个无参的构造方法, 构造方法中没有任何的代码!
-
如果自行编写了任意一个构造器, 则编译器不会再自动生成无参的构造方法。
无参构造方法和有参构造方法:
- 无参构造方法: 生成初始化对象,属性都是默认值,系统默认提供
- 有参构造方法:生成初始化对象,属性都是实参提供,当你显示声明了有参构造方法
-
-
定义格式
-
与普通方法基本相同, 区别在于: 方法名称必须与类名相同, 没有返回值类型的声明 ;
-
建议自定义无参构造方法,不要对编译器形成依赖,避免错误发生。
-
当类中有非常量成员变量时,建议提供两个版本的构造方法,一个是无参构造方法,一个是全属性做参数的构造方法。
-
当类中所有成员变量都是常量或者没有成员变量时,建议不提供任何版本的构造。
-
/*
苹果树种类
*/
public class AppleTree {
public String name="苹果树"; //名称
public String brand;//品种
public int growTime=10; //生长期
public int harvesTime=2; //采摘期
public int numsOfFruits=100; //果实数量
public String status="生长期"; //生长状态
public boolean isHarvested=false; //是否已采摘
/*
* 类的无参构造方法
* */
public AppleTree(){
this.brand="金帅";
}
public void print(){
System.out.println("*****作物特性******");
System.out.println("您种植了"+this.name+"天");
System.out.println("生长期"+this.growTime+"天+采摘期"+this.harvesTime+"天+果实数量为"+this.numsOfFruits+"+现在处于"+this.status);
System.out.println(this.name+"属于水果物种,品种"+this.brand);
}
public static void main(String[] args) {
AppleTree appleTree=new AppleTree();
appleTree.print();
}
}
这里,AppleTree()方法就是类的无参构造方法
当执行 AppleTree appleTree=new AppleTree(); 就会执行自定义构造方法
重载(Overload)
方法的重载
-
方法名相同
-
参数类型和个数不同
-
修饰符和返回值类型不同
-
方法的重载与返回值无关;
-
方法的重载 ,可以让我们在不同的需求下, 通过传递不同的参数调用方法来完成具体的功能。
当你显示声明了有参构造方法,系统不提供无参构造方法,此时可以自定义一个无参构造方法
类的成员方法和构造方法都可以进行重载
int sum(int x, int y){
int z = x + y;
return z;
}
double sum(double x, double y){
double z = x + y;
return z;
}
构造方法的重载
- 一个类, 可以存在多个构造方法 ;
- 参数列表的长度或类型不同即可完成构造方法的重载 ;
- 构造方法的重载 ,可以让我们在不同的创建对象的需求下, 调用不同的方法来完成对象的初始化 ;
重写(Override):
- 参数列表必须完全与被重写的方法相同;
- 返回类型必须完全与被重写的返回类型相同;
- 访问权限不能比父类被重写的方法的访问权限更低。例如父类方法为public,子类就不能为protected;
- 父类的成员方法只能被它的子类继承;
- 声明为static和private的方法不能被重写,但是能够被再次声明
public class Demo{
public static void main(String[] args){
Student student=new Student();
student.say();
}
}
class Person{
public void say(){
System.out.println("锄禾日当午,汗滴禾下土。");
}
}
class Student extends Person{
public void say(){
System.out.println("锄禾日当午,玻璃好上霜。要不及时擦,整不好得脏。");
}
}
//结果为:
//锄禾日当午,玻璃好上霜。要不及时擦,整不好得脏。
重写与重载的区别
- 方法重写设涉及的是子类和父类之间的同名方法,要求方法名相同,参数列表不同,与返回值类型和访问修饰符无关
- 方法重载涉及同一个类中的同名方法,要求方法名相同,参数列表不同,与返回值类型和访问修饰符无关
关键字:
this:
- 调用类中的属性
- 调用类中的方法或构造方法 ,注意:在一个构造方法中,调用另一个构造方法时,调用的代码必须编写在构造方法的第一行。
- 表示当前对象
- 调用成员变量,方法
- 调用已定义的构造方法
因为this关键字是在对象内部指代自身的引用,所以它只能调用实例变量,实例方法和构造方法,不能调用类变量和方法,也不能调用局部变量
this关键字主要有三种应用场景
使用this关键字调用成员变量,解决成员变量和局部变量的同名冲突
public AppleTree(String brand){ this.brand=brand; //成员变量和局部变量同名,必须使用this关键字 }
使用关键字this调用成员方法
public void changeName(String name){ this.name=name; this.print(): }
使用this关键字调用已定义的构造方法
class Person{
private String name;
private int age;
Person(){
//调用下面的构造方法,如果下面还有代码,必须写在第一行
this("张三",12);
}
Person(String name,int age){
//调用类中的属性
this.name=name;
this.age=age;
}
}
static:
Java中,是否可以通过类名直接访问类的成员?
- 使用static关键字可以实现
- 用 static 修饰的属性、方法和代码块属于它所在的类,由这个类创建的所有对象可以共用同一个static成员
- 使用static 修饰的属性称为静态变量或类变量
- 没有使用 static 修饰的属性称为实例变量
Java方法分类
- 静态方法(又称类方法):使用 static 关键字修饰的方法
- 实例方法:未使用 static 关键字修饰的方法
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9Zijw4LO-1640223636859)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211223084500805.png)]
static特点
- 一个类中包含多个static修饰符
- 当类被加载(初始化对象)按顺序执行
- 只会执行一遍
- 静态属性会被多个对象共享
使用static修饰与非static修饰的类成员有哪些不同?
static
、非private修饰
非
static、private修饰
属性
类属性、类变量
实例属性、实例变量
方法
类方法
实例方法
调用方式
类名.属性 类名.方法() 对象.属性 对象.方法()
对象
属性对象方法()
归属
类
单个对象
概述:
-
表示“静态”的意思,可以用来修饰成员变量和成员方法
-
static的主要作用在于创建独立于具体对象的域变量或者方法 。
- 静态成员 在类加载时加载并初始化 ;
- 无论一个类存在多少个对象 , 静态的属性, 永远在内存中只有一份( 可以理解为所有对象公用 ) ;
- 在访问时: 静态不能访问非静态 , 非静态可以访问静态 ;
- 静态修饰的方法,被调用时,有可能对象还未创建 ;
final:
final用于修饰属性(类里定义的标识符称为属性)和变量(方法体里定义的标识符成为变量:
- 通过final修饰的属性和变量都是常量,就是不能再次赋值的变量或属性 ;
- final修饰的局部变量,只能赋值一次(可以先声明后赋值);
- final修饰的成员属性,必须在声明时赋值 ;
- 全局常量(public static final)可以在任何位置被访问 ;
- 常量的命名规范:由一个或多个单词组成,单词之间必须使用下划线隔开,所有字母大写,例如:SQL_INSERT ;
final用于修饰类:
final修饰的类,不能被继承。
final用于修饰方法:
final修饰的方法,不能被子类重写。
封装
将类的某些信息隐藏在类内部,不允许外部程序直接访问
而是通过该类提供的方法来实现对隐藏信息的操作和访问
实现封装的具体步骤如下:
- 修改属性的可见性来限制属性的访问
- 为每个属性创建一对赋值(setter)方法和取值(getter)方法,用于对这些属性的存取
- 在赋值方法中,可以加入对属性的存取控制语句
//我们观察如下代码:
class Person{
private String name ; // 表示姓名
private int age ; // 表示年龄
void tell(){
System.out.println("姓名:" + name + ";年龄:" + age) ;
}
};
public class Demo{
public static void main(String args[]){
Person per = new Person() ;
per.name = "张三" ;
per.age = -30 ;
per.tell() ;
}
};
//以上的操作代码并没有出现了语法错误,但是出现了逻辑错误 (年龄-30岁) 在开发中, 为了避免出现逻辑错误, 我们建议对所有属性进行封装,并为其提供setter及getter方法进行设置和取得 操作。 修改代码如下:
class Person{
private String name ; // 表示姓名
private int age ; // 表示年龄
void tell(){
System.out.println("姓名:" + getName() + ";年龄:" + getAge()) ;
}
public void setName(String str){
name = str ;
}
public void setAge(int a){
if(a>0&&a<150) age = a ;
}
public String getName(){
return name ;
}
public int getAge(){
return age ;
}
};
public class OODemo10{
public static void main(String args[]){
Person per = new Person() ;
per.setName("张三") ;
per.setAge(-30) ;
per.tell() ;
}
};
封装的好处:
- 隐藏类的实现细节
- 只能通过规定方法访问数据
- 方便加入控制语句
- 方便修改
封装的步骤:
- 修改属性的可见性—改为private
- 创建公有的get/setter方法–用于属性的读写
- 在get/setter方法中加入属性控制语句–对属性值的合法性进行判断
访问修饰符:
Java中,如何限制和制约类之间的访问关系?
- 包
- 访问修饰符
类的访问修饰符
修饰符 作用域 |
同一包中 |
非同一包中 |
---|---|---|
public | 可以使用 | 可以使用 |
默认修饰符 | 可以使用 | 不可以使用 |
类成员的访问修饰符:
修饰符 作用域 |
同一类中 |
同一包中 |
子类中 |
外部包 |
---|---|---|---|---|
private | 可以使用 | 不可以使用 | 不可以使用 | 不可以使用 |
默认修饰符 | 可以使用 | 可以使用 | 不可以使用 | 不可以使用 |
protected | 可以使用 | 可以使用 | 可以使用 | 不可以使用 |
public | 可以使用 | 可以使用 | 可以使用 | 可以使用 |
类成员的访问修饰符:
修饰符 作用域 |
同一类中 |
同一包中 |
子类中 |
外部包 |
---|---|---|---|---|
private | 可以使用 | 不可以使用 | 不可以使用 | 不可以使用 |
默认修饰符 | 可以使用 | 可以使用 | 不可以使用 | 不可以使用 |
protected | 可以使用 | 可以使用 | 可以使用 | 不可以使用 |
public | 可以使用 | 可以使用 | 可以使用 | 可以使用 |
总结