Java笔记–类与对象
面向过程:当要实现一个功能的时候,每一个具体步骤都要亲历亲为,详细处理每一个细节。
面向对象:当实现一个功能不关心具体步骤,而是我一个已经具有该功能的人来帮我做事。
类:一组相关属性和关系行为的集合,可以看成是一类事物的模板,使用事务的属性特征和行为特征来描述该类事务
- 属性:该事务的状态信息/特征描述信息。
- 行为:该事务能够做什么。
类与对象的关系
- 类是对一类事物的描述,是抽象的。
- 对象是一类实物的实例,是具体的。
- 类是对象的模板,对象是类的实体。
类的定义
定义一个类用来模拟”学生“事物,其中就有两个组成成分:
属性(是什么):
姓名
年龄
行为(能做什么):
吃饭
睡觉
学习
对应到java的类当中:
成员变量(属性):
String name//姓名
int age//年龄
成员方法(行为)://没有static
public void eat () {};
public void sleep() {};
public void study() {};
【注意事项】
1、成员变量是直接定义在类当中的,在方法外边。
2、成员方法不要写static关键字。
public class Student {
//成员变量
String name;//方法外定义的变量
int age;
//成员方法
public void eat(){
System.out.println("吃饭饭!");
}
public void sleep(){
System.out.println("睡觉觉!");
}
public void study(){
System.out.println("学习!");
}
}通常情况下,一个类并不能直接使用,需要根据类创建一个对象才能使用。
1、导包:指出需要使用的类在什么位置。
import 包名称.类名称
import cn.cikeshijia.Demo02.Student
对于和当前类属于同一个包的情况,可以省略导包语句不写。
2、创建,格式
类名称 对象名 = new 类名称();//对象名可以看作变量名
Student stu = new Student();//根据Student类创建了一个stu的对象
3、使用:分两种情况
使用成员变量:对象名.成员变量名
使用成员方法 :对象名.成员方法名(参数)
(也就是想用谁,就用对象名.谁)
【注意事项】
==如果成员变量没有进行赋值==,那么将有一个默认值,规则和数组一样。
//1、导包
//我需要的Student类,和我自己位于同一个包下,所以额可以省略导包语句不写
import cn.cikeshijia.Demo02.Student;
public class Demo02Student {
public static void main(String[] args) {
//2、创建
//类名称 对象名 = new 类名称();
Student stu =new Student();
//3、使用
//对象名.成员变量名
System.out.println(stu.name);
System.out.println(stu.age);
stu.name = "鞠婧祎";
stu.age = 29;
System.out.println(stu.name);
System.out.println(stu.age);
System.out.println("===========");
//对象名。成员方法名()
stu.eat();
stu.sleep();
stu.study();
}
}面向对象的三大特征:封装、继承、多态
封装
封装性在java当中的体现。
1、方法就是一种封装
2、关键字Privote也是一种封装
用private关键字将需要保护的成员变量进行修饰
一旦使用了private关键字进行修饰,那么本类中当然可以随意访问。
但是!超出了本类范围之外就不能随意访问了。
间接访问private成员变量,就是定义一对儿getter/setter方法。
在方法中可以对输入/输出数据进行处理。
必须叫setxxx或者是getxxx命名规则
- 对于getxxx来说,不能有参数,返回值类型和成员变量对应
- 对于setxxx来说,不能有返回值,参数类型和成员变量对应
this关键字
当方法的局部变量和类的成员变量重名的时候,根据“就近原则”,优先使用局部变量。
当需要访问本类中的成员变量,需要使用格式:
this.成员变量名
++通过谁调用的方法,谁就是this++
构造方法
构造方法是专门用来创建对象的方法,当我们通过关键字new来创建对象的时候其实就是在构造方法。
//格式
public 类名称(参数类型 参数名称){
方法体
}
【注意事项】
1、构造方法名称必须与所在类名称完全一样,就连大小写也要一样。
2、构造方不要写返回值类型,连void都不写。
3、构造方法不能return一个具体的返回值。
4、如果没有编写任何构造方法,那么编译器将会默认赠送一个构造方法,没有参数、方法体,什么都不做。
5、一旦编写了至少一个构造方法,那么编译器将不在赠送。
6、构造方法也是可以重载的。
重载:方法名称相同,参数列表不同。
一个标准类
一个标准的类通常要拥有下面四个组成部分:
1、所有的成员变量都要用 ==private== 关键字修饰
2、为每一个成员变量编写一对儿 ==getter/setter== 方法
3、编写一个无参数的构造方法
4、编写一个全参数的构造方法
一个标准的类也叫做Java Bean。
Student类
/*
一个标准的类通常要拥有下面四个组成部分:
1、所有的成员变量都要用private关键字修饰
2、为每一个成员变量编写一对儿getter/setter方法
3、编写一个无参数的构造方法
4、编写一个全参数的构造方法
一个标准的类也叫做Java Bean
*/
public class Student {
private String name;//姓名
private int age;//年龄
public Student() {//无参构造函数
}
public Student(String name, int age) {//全参构造函数
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
主函数
public class Demo03Student {
public static void main(String[] args) {
Student stu1 =new Student();//创建
stu1.setName("迪丽热巴");
stu1.setAge(18);
System.out.println("姓名:"+stu1.getName()+"。年龄:"+ stu1.getAge());
System.out.println("===========华丽的分割线=========");
Student stu2 = new Student("古力娜扎",20);
System.out.println("姓名:"+stu2.getName()+"。年龄:"+ stu2.getAge());
stu2.setAge(21);
System.out.println("姓名:"+stu2.getName()+"。年龄:"+ stu2.getAge());
}
}
继承
继承是多态的前提,没有继承就没有多态。
继承主要解决的问题就是共性抽取
父类(基类、超类)
子类(派生类)
继承关系当中的特点:
- 子类可以拥有父类的“内容”
- 子类还可以拥有自己专有的内容。
在继承的关系中,“子类就是一个父类”。也就是说,子类可以被当作父类看待
例如父类是员工,子类是讲师,那么讲师就是一个“员工”。关系:is-a。
定义方法
//定义父类格式:(一个普通的类定义)
public class 父类名称{
//……
}
//定义子类格式:
public class 子类名称 extends 父类名称{
//……
}多个变量重名时,变量调用
在父子类的继承关系当中,如果成员变量==重名==,则创建子类对象时,访问有两种方式:
- 直接通过子类对象访问成员变量:
等号左边是谁,就优先用谁,没有则向上找。 - 间接通过成员方法访问成员变量:
该方法属于谁, 就优先用谁,没有则向上找。
/*
局部变量: 直接写成员变量名
本类的成员变量: this.成员变量名
父类的成员变量: super. 成员变量名
*/访问成员方法的规则:
在父子类的继承关系当中,创建子类对象,访问成员方法的规则:
创建的对象是谁,就优先用谁,如果没有则向上找。
【注意事项】
++无论是成员方法还是成员变量,如果没有都是向上找父类,绝对不会向下找子类的。++
重写(Override)
概念:在继承关系当中,方法的名称一样,参数列表也一样。
重写(Override) : 方法的名称一样,参数列表【也一样】。覆盖/覆写
重载(Overload) : 方法的名称一样,参数列表【不一样】。
方法的覆盖重写特点:
创建的是子类对象,则优先用子类方法。
方法覆盖重写的注意事项:
==必须保证父子类之间方法的名称相同,参数列表也相同。==
@Override:写在方法前面,用来检测是不是有效的正确覆盖重写。
这个注解就算不写,只要满足要求,也是正确的方法覆盖重写。==子类方法的返回值必须【小于等于】父类方法的返回值范围。==
小扩展提示: java. lang. object类是所有类的公共最高父类(祖宗类),
java. lang.String就是object的子类。
- ==子类方法的权限必须【大于等于【父类方法的权限修饰符。==
小扩展提示: public > protected > (default) > private
备注: (default)不是关键字default,而是什么都不写,留空。
覆盖重写的应用
对于方法的重写,需要新功能,又不能舍弃旧功能。定义一个子类方法,将需要的新的方法加入,并引用子类方法。super.旧方法( );
构造方法
继承关系中,父子类构造方法的访问特点:
- 子类构造方法当中有一个默认隐含的“super()”调用,所以一定是先调用的父类构造,后执行的子类构造。
- 子类构造可以通过super关键字来调用父类重载构造。
- super的父类构造调用,必须是子类构造方法的第一个语句。 不能一个子类构造调用多次super构造。
总结:
子类必须调用父类构造方法,不写则赠送super();写了则用写的指定的super调用,super只能有一个,还必须是第一个。
super关键字的用法有三种:
- 在子类的成员方法中,访问父类的成员变量。
- 在子类的成员方法中,访问父类的成员方法。
- 在子类的构造方法中,访问父类的构造方法。
this关键字的用法有三种:
- 在本类的成员方法中,访问本类的成员变量。
- 在本类的成员方法中,访问本类的另- -个成员方法。
- 在本类的构造方法中,访问本类的另一个构造方法。
==在第三种用法当中要注意:==
A. this(… )调用也必须是构造方法的第一个语句, 唯一一个 。
B. super和this两种构造调用,不能同时使用。
总结
- Java语言是==单继承==的。
一个类的直接父类只能有唯一一个。 - Java语言可以==多级继承==。
我有一个父亲,我父亲还有一个父亲,也就是爷爷。
java.lang.object:Java中的祖宗类。 - 一个子类的直接父类是唯一的,但是一个父类可以拥有很多个子类。
可以有很多个兄弟姐妹,生二胎。
抽象
==抽象方法:== 就是加上abstract关键字,然后去掉大括号,直接分号结束。
==抽象类:== 抽象方法所在的类,必须是抽象类才行。在class之前写上abstract即可。
如何使用抽象类和抽象方法:
- 不能直接创建new抽象类对象。
- 必须用一个子类来继承抽象父类。
- 子类必须覆盖重写抽象父类当中所有的抽象方法。
覆盖重写(实现): 子类去掉抽象方法的abstract关键字,然后补上方法体大括号。
4. 创建子类对象进行使用。
public abstract class Animal {
//这是一个抽象方法,代表吃东西,但是具体吃什么(大括号的内容)不确定。
public abstract void eat();
//这是普通的成员方法
public void normalMethod() {
}
public class Cat extends Animal {
@Override
public void eat(){
System. . out. println( "猫吃鱼");
}
注意事项
关于抽象类的使用,以下为语法上要注意的细节,虽然条目较多,但若理解了抽象的本质,无需死记硬背。
- 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
理解: 假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。
- 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
理解: 子类的构造方法中,有默认的super(),需要访问父类构造方法。
- 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
理解: 未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。
- 抽象类的子类,必须重写抽象父类中==所有的==抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。
理解: 假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。
多态
代码当中体现多态性,其实就是一句话。父类引用指向子类对象。
继承是多态的前提。
多态指:一个方法有多种状态。
格式:
父类名称 对象名= new子类名称();
或者:
接口名称对象名= new实现类名称();此时就是将子类对象当作父类进行使用
访问成员对象
访问成员变量的两种方式:
- 直接通过对象名称访间成员变量,看等号左边是谁,优先用谁,没有则向上找
- 间接通过成员方法访问成员变量,看该方法属于谁,优先用谁,没有则向上找
访问成员方法
在多态的代码中,成员方法的访问规则是:
看new的是谁,就优先用谁,没有则向上找
口诀:编译看左边,运行看右边
- 在编译时,看父类方法,如果父类方法没有,就会报错。在运行时,看子类方法,运行子类方法里面有的对象。
- 也就是说,只有子类里面对父类重写的方法才有用。
注:成员变量编译看左边,运行还看左边。
使用多态的好处
//如果不用多态,只用子类,那么写法是:
Teacher one = new Teacher();
one.work();//讲课
Assistant two = new Assistant(;
two.work();//辅导我现在唯—要做的事情,就是调用work方法,其他的功
能不关心。
//如果使用多态的写法,对比一下:
Employee one = new Teacher();
one.work();//讲课
Employee two = new Assistant();
two.work()://辅导好处:无论右边new的时候换成哪个子类对象,等号左边调用方法都不会变化。
对象的 ++向上转型++
对象的向上转型,其实就是多态写法;格式:
父类名称 对象名 = new 子类名称();含义:右侧创建了一个子类对象,把他当作父类来看待使用。
Animal animal = new Cat();//创建了一只猫当做动物看待【注意事项】==向上转型一定是安全的==。从小范围转向了大范围,从小范围的猫,向上转换成为更大范围的动物。
类似于:
double number = 100;//int-->double自动类型转换。向上转型一定是安全的,没有问题的,正确的。但是也有一个 弊端: 对象—旦向上转型为父类,那么就无法调用子类原本特有的内容。
解决方案:用对象的向下转型【还原】
对象的 ++向下转型++
对象的向下转型,其实是一个【还原】的动作。
格式:
子类名称 对象名 = (子类名称) 父类对象;含意:将父类对象,【还原】成为本来的子类对象。
Animal animal = new Cat();//本来是猫,向上转型成为动物
Cat cat = (Cat) animal://本来是猫,已经被当做动物了,还原回来成为本来的猫注意事项:
a. 必须保证对象本来创建的时候,就是猫,才能向下转型成为猫。
b. 如果对象创建的时候本来不是猫,现在非要向下转型成为猫,就会报错(编译不会报错,运行会出现异常[ClassCastException])。
类似于:
int number = (int)10.0;//int-->double自动类型转换。
int number = (int)10.5;//不可以,精度损失instanceof关键字
如何才能知道一个父类引用的对象,本来是什么子类?
格式
对家instanceof类型
这将会得到一个booLear值结果,也就是判断前面的对象能不能当做后面类型的实例。
public class Demoe2Instanceof {
public static void main(String[] args){
Animal animal = new Cat();//本来是一只猫
animal.eat();//猫吃鱼
//如果希望掉用子类特有方法,需要向下转型
//判断一下交类引用animat本来是不是Dog
if(animal instanceof Dog) {
Dog dog = (Dog)animal;
dog.watchHouse();
}
//判断一下animaL本来是不是cat
if (animal instanceof Cat) {
cat cat = (cat)animal;
cat.catchMouse();
}
}
}
向下转型一定要进行instanceof判断以免出错
静态字段与静态方法(利用static关键字)
如果一个成员变量使用了static关键字,那么这个变量不再属于对象自己,而是属于所在的类。多个对象共享同一份数据。
public class Demo01StaticField {
public static void main(String[] args) {
Student one = new Student("郭靖" , 19);
one.room = "101教室";
System.out.println("姓名:"+ one.getName() +
", 年龄:" + one.getAge() + ", 教室:"+one.room +
",学号:" + one.getId());
Student two = new Student("黄蓉",16);
System.out.println("姓名:"+ two.getName() +
", 年龄:" + two.getAge() + ", 教室:"+one.room +
",学号:" + two.getId());
}
}Student类
public class Student {
private int id;//学号
private String name;//姓名
private int age;//年龄
static String room;//教室
private static int idCounter = 0;//学号计数器,每当new了一个新对象的时候计数器++
public Student() {
this.id = ++idCounter;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
this.id = ++idCounter;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
static修饰成员
一旦使用static修饰成员方法,那么这就成为了静态方法。静态方法不属于对象,而是属于类的。
如果没有static关键字,那么必须首先创建对象,然后通过对象才能使用它。
如果有了static关键字,那么不需要创建对象,直接就能通过类名称来使用它。
无论是成员变量,还是成员方法。如果有了static,都推荐==使用类名称进行调用==。
- 静态变量:类名称.静态变量
- 静态方法:类名称.静态方法()
【注意事项】
- 静态不能直接访问非静态。
原因:因为在内存当中是[先]有的静态内容,[后] 有的非静态内容。
“先人不知道后人,但是后人知道先人。” - 静态方法当中不能用this.
原因: this代表当前对象, 通过谁调用的方法,谁就是当前对象。
public class Demo02StaticMethod {
public static void main(String[] args) {
MyClass obj = new MyClass(); //首先创建对象
//然后才能使用没有static关键字的内容
obj.method();
//对于静态方法来说,可以通过对象名进行调用,也可以直接通过类名称来调用。
obj.methodStatic(); //正确,不推荐,这种写法在编译之后也会被javac翻译成为“类名称.静态方法名”
MyClass . methodStatic(); //正确,推荐
//对于本来当中的静态方法,可以省略类名称
myMethod();
Demo02StaticMethod.myMethod(); //完全等效
}
public static void myMethod() {
System. out. println("自己的方法! ");
}
}
MyClass类
public class MyClass {
int num; //成员变量
static int numStatic; //静态变量
//成员方法
public void method() {
System. out . println("这是一个成员方法。 ");
//成员方法可以访问成员变量
System. out . println(num);
//成员方法可以访问静态变量
System. out . println(numStatic);
}
//静态方法
public static void methodStatic() {
System. out. println("这是一个静态方法。");
//静态方法可以访问静态变量
System. out. println(numStatic);
//静态不能直接访问非静态[重点]
// System. out. println(num); //错误写法!
//静态方法中不能使用this关键字。
// System. out.println(this); //错误写法!
}
}
静态代码块
静态代码块的格式是:
public class 类名称{
static {
//静态代码块的内容
}
}特点:当第一次用到本类时,静态代码块执行 ==唯一== 的一次。
静态内容总是优先于非静态,所以静态代码块==比构造方法先执行==。
静态代码块的典型用途:
++用来一次性地对静态成员变量进行赋值。++
public class Demo04Static {
public static void main(String[] args) {
Person one = new Person();
Person two = new Person();
}
}
Person类
public class Person {
static{
System.out.println("静态代码块执行!");
}
public Person() {
System.out.println("构造方法执行! ");
}
}
final
finaL关键字代表最终的、不可改变的。
常见四种用法:
- 可以用来修饰—个类
- 可以用来修饰—个方法
- 还可以用来修饰一个局部变量
- 还可以用来修饰一个成员变量
1. 可以用来修饰—个类
当finaL关键字用来修饰一个类的时候,格式:
public finaL cLass 类名称{
//...
}含义:当前这个类不能有任何的子类。(太监类〕
注意:一个类如集是final的,那么其中所有的成员方法都无法进行覆盖重写(因为没儿子)
public final class MyClass f*extends object*/ {
2. 可以用来修饰—个方法
当final关键字用来修饰一个方法的时候,这个方法就是最终方法,也就是不能被覆盖重写。
格式:
修饰符finol 返回值类型 方法名称(参数列表){
//方法体
}【江意事项】
对于类、方法来说,abstract关键字和final关键字不能同时使用,因为矛盾。
3. 还可以用来修饰一个局部变量
一旦使用finaL用来修饰局部变量,那么这个变量就不能进行更改。
“一次赋值,终生不变”
定义的实体存储地址不发生改变。
pubiic class DemoFinal { public static void main(String[] args) { int num1 = 10; System.out.println(num1);//10 num1 = 20; System.out.println(num1); //20 //一旦使用final用来修饰局部变量,那么这个变量就不能进行更改。 //“一次赋值,终生不变” final int num2 = 20; System.out.println(num2); //20 // num2 =25;//错误写祛!不能改变! // nun2 = 20; //错误写法! //正确写法!只要保证有唯——次赋值即可 final int num3; num3 = 30; //对于基本类型来说,不可变说的是变量当中的数据不可改变 //对于引用类型来说,不可变说的是变童当中的地址值不可改变
}
### 4. 可以用来修饰一个成员变量
对于成员变量来说,如果使用final关键字修饰,那么这个变量也照样是不可变。
1. 由于成员变量具有默认值,所以用了final之后必须手动赋值,不会再给默认值了。
2. 对于final的成员变量,要么使用直接赋值,要么通过构造方法赋值。二者选其一。
3. 必须保证类当中所有重载的构造方法,都最终会对final的成员变量进行赋值。public class Person {
private final String name /* =“鹿晗" */;//方法一
public Person(){
name = "关晓彤";
}
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
//public void setName(String name) {
// this.name = name;
//}}