目录
2.3 构造方法中,可以通过this调用其他构造方法来简化代码
2.3 使用import static导入包中静态的方法和字段
一. 面向对象的初步认识
1.什么是面向对象
Java是一门纯面向对象的语言(Object Oriented Program,简称OOP),在JAVA中,一切皆为对象。面向对象是解决问题的一种思想,主要依靠对象之间的交互完成一件事情。
举例:假设,制造一辆车,该车由车身框架,发动机,轮胎三大部分过程。
有两种制造方式:
第一种:所有部分均自己制造,一步一步制造出每一个部位的零件,缺一不可。
第二种:找三家不同的厂商,分别制造车身框架,发动机,轮胎。我只需要给他们所需制造的规格,就能得到每个部位的成品。
这两种方式都可以制造出车,第一种就是 “面向过程” 思想,第二种就是 “面向对象思想”。将 "制造车" 分给不同的 “对象” 分别完成一个 “部分”。
面向对象思想,就是将事物,抽象化,分为几个大部分,作为对象。也可以理解为,变成一个模板,复印机。只需往里面投递 "原料" 就可以得到成品。至于模板内部如何运作,不考虑。
二. 类的定义与使用
1.类的简单认识
类是用来对一个实体(对象)来进行描述的,主要描述该实体(对象)具有哪些属性(外观尺寸等),哪些功能(用来干啥),描述完成后计算机就可以识别了。
比如:将 学生 作为对象。则这个学生类,有姓名,性别,学号,班级等等。
2.类的定义格式
在java中定义类时需要用到class关键字,具体语法如下:
// 创建类 class ClassName{ field; // 字段(属性) 或者 成员变量 method; // 行为 或者 成员方法 }
class为定义类的关键字,ClassName为类的名字,{}中为类的主体。
类中包含的内容称为类的成员。成员又可分为:
1.成员属性(还可细分为:静态属性,非静态属性)主要是用来描述类的,称之为类的成员属性或者类成员变量。
2.成员方法(还可细分为:静态方法,非静态方法)主要说明类具有哪些功能,称为类的成员方法。
举例:将 生活中的 手机 对象化。定义一个手机类
采用Java语言将洗衣机类在计算机中定义完成,经过javac编译之后形成.class文件,在JVM的基础上计算机就可以识别了。
【注意事项】:
1.类名注意采用大驼峰定义
2.public是访问限定符,后下面修饰符会详细解
3. 类的实例化
类是一种新定义的类型,有了自定义的类型之后,就可以使用这些类来定义实例(或者称为对象)。用 类 类型创建对象的过程,称为类的实例化,在java中采用new关键字,配合类名来实例化对象。
举例:已经有了一个 狗类
在 main 中实例化 狗类,并使用它的的属性:
class PetDog {
public String name;//名字
public String color;//颜色
// 狗的属性
public void barks() {
System.out.println(color+"的"+name + ": 旺旺旺~~~");
}
// 狗的行为
public void wag() {
System.out.println(color+"的"+name + ": 摇尾巴~~~");
}
}
public class Demo {
public static void main(String[] args) {
PetDog petDog = new PetDog();
petDog.name="旺财";
petDog.color="黑色";
petDog.barks();
petDog.wag();
/*
运行结果:
黑色的旺财:旺旺旺~~~
黑色的旺财:摇尾巴~~~
*/
}
}
【注意事项】
1.new 关键字用于创建一个对象的实例.
2.使用 . 来访问对象中的属性和方法.
3.同一个类可以创建对个实例.
4.类和对象的说明
1. 类只是一个模型一样的东西,用来对一个实体进行描述,限定了类有哪些成员.
2. 类是一种自定义的类型,可以用来定义变量.
3. 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量
4. 做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑 存在同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间。
三. this引用(代表当前对象的引用)
1.为什么需要this
举例:有一个日期类,成员属性有 年,月,日。通过一个成员方法对属性赋值。
class Date{
public int year; //年
public int month; //月
public int day; //日
public void setDay(int year, int month, int day){ //从外部通过调用该方法,对年月日赋值
year = year;
month = month;
day = day;
}
public void printDate(){
System.out.println(year+"年"+month+"月"+day+"日");
}
public static void main(String[] args) {
Date date = new Date();
date.setDay(2023,8,19);
date.printDate();
/*
预想运行结果:2023年8月19日
实际运行结果:0年0月0日
*/
Date date2 = new Date();
date2.setDay(2020,10,1);
date2.printDate();
/*
预想运行结果:2020年10月1日
实际运行结果:0年0月0日
*/
}
}
通过上述代码,我们发现程序没有达到,我们预想的结果,这是因为这里出现了两个问题:
1. 当我们在调用setDay方法是,形参名 与 成员属性名 相同了,那函数体中到底是谁给谁赋值?成员变量给成员变量?参数给参 数?参数给成员变量?成员变量参数?
2. 有两个对象都在调用setDate和printDate函数,但是这两个函数中没有任何有关对象的说明,setDate和printDate函数如何知 道打印的是那个对象的数据呢?
所以,就有了 this 关键字,解决这个问题
2.this 的使用
this引用指向当前对象(成员方法运行时调用该成员方法的对象),在成员方法中所有成员变量的操作,都是通过该引用去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
还是用上面 日期 类举例:加上this之后,程序达到预想结果。
class Date{
public int year; //年
public int month; //月
public int day; //日
public void setDay(int year, int month, int day){ //从外部通过调用该方法,对年月日赋值
this.year = year;
this.month = month;
this.day = day;
}
public void printDate(){
System.out.println(this.year+"年"+this.month+"月"+this.day+"日");
}
public static void main(String[] args) {
Date date = new Date();
date.setDay(2023,8,19);
date.printDate();
/*
预想运行结果:2023年8月19日
实际运行结果:2023年8月19日
*/
Date date2 = new Date();
date2.setDay(2020,10,1);
date2.printDate();
/*
预想运行结果:2020年10月1日
实际运行结果:2020年10月1日
*/
}
}
【注意事项】
1. this的类型:对应类类型引用,即哪个对象调用就是哪个对象的引用类型
2. this只能在"成员方法"中使用
3. 在"成员方法"中,this只能引用当前对象,不能再引用其他对象
4. this是“成员方法”第一个隐藏的参数,编译器会自动传递,在成员方法执行时,编译器会负责将调用成员方法对象的引用传递给该成员 方法,this负责来接收
在代码层面来简单演示--->注意:下图右侧中的Date类也是可以通过编译的
四. 对象的构造方法和初始化
1. 默认初始化
局部变量,在未初始化的情况下使用,编译器会报错。但是成员变量则不会,比如:
public class Demo {
int year;
int month;
int day;
public static void main(String[] args) {
Demo demo =new Demo();
//成员变量并未初始化,但是编译器不会报错
System.out.println(demo.year);
System.out.println(demo.month);
System.out.println(demo.day);
/*
int a;
System.out.println(a); 这里的输出 a, 就会报错
*/
}
}
这是因为,当 new 一个新的对象之后,从程序层面上来看,我们只执行了( Demo demo = new Demo() ) 这一步
实际上 ,在JVM层面上,这一步又被划分了好几步:
1. 检测对象对应的类是否加载了,如果没有加载则加载
2. 为对象分配内存空间
3. 处理并发安全问题
比如:多个线程同时申请对象,JVM要保证给对象分配的空间不冲突
4. 初始化所分配的空间
即:对象空间被申请好之后,对象中包含的成员已经设置好了初始值,比如:
数据类型 | 默认值 |
byte | 0 |
char | '\u0000' |
short | 0 |
int | 0 |
long | 0L |
boolean | false |
float | 0.0f |
double | 0.0 |
reference | null |
5. 设置对象头信息
6. 调用构造方法,给对象中各个成员赋值
2. 构造方法
2.1 构造方法的定义和使用
构造方法(也称为构造器) 是一个:特殊的成员方法,名字必须与类名相同,在创建对象时,由编译器自动调用,并且在整个对象的生命周期内只调用一次。
语法格式( 构造方法分为 带参数 和不带参数 两种 ):
public 类名 (){
代码;
}举例:
public class Demo { int year; int month; int day; public Demo(){ //不带参数 System.out.println("不带参数的构造方法"); } public Demo(int year,int month,int day){ //带参数 this.year=year; this.month=month; this.day=day; System.out.println("带参数的构造方法"); }
【注意事项】
1. 构造方法的作用就是对对象中的成员进行初始化,并不负责给对象开辟空间。
2. 名字必须与类名相同
3. 没有返回值类型,设置为void也不行
4. 创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次(相当于人的出生,每个人只能出生一次)
5. 构造方法可以重载(用户根据自己的需求提供不同参数的构造方法)
上述两个构造方法:名字相同,参数列表不同,因此构成了方法重载。
有了构造方法,就可以实现在 创建一个新的对象时,根据需求,实现所想要的初始化。
public class Demo {
int year;
int month;
int day;
public Demo(int year,int month,int day){ //带参数的构造方法
this.year=year;
this.month=month;
this.day=day;
}
public void printDemo(){
System.out.println(year+"年"+month+"月"+day+"日");
}
public static void main(String[] args) {
/*
在 new 一个新的对象时,传递不同的参数,可以得到不同的结果。
*/
Demo demo1=new Demo(2023,8,15); //在创建对象时,编译器会自动调用构造方法
Demo demo2=new Demo(2022,6,1); //传参给构造方法,通过构造方法完成初始化
Demo demo3=new Demo(2020,4,16);
demo1.printDemo(); //输出:2023年8月15日
demo2.printDemo(); //输出:2022年6月1日
demo3.printDemo(); //输出:2020年4月16日
}
}
2.2 编译器默认生成无参构造方法
如果,用户没有定义构造方法,编译器会生成一份默认的构造方法,生成的默认构造方法一定是无参的:
public class Date { int year; int month; int day; public void printDate(){ System.out.println(year + "-" + month + "-" + day); } public static void main(String[] args) { Date d = new Date(); d.printDate(); } }
但是,一旦用户定义,编译器则不再生成
public class Date { int year; int month; int day; public Date(int year, int month, int day) { this.year = year; this.month = month; this.day = day; } public void printDate(){ System.out.println(year + "-" + month + "-" + day); } public static void main(String[] args) { // 如果编译器会生成,则生成的构造方法一定是无参的 // 则此处创建对象是可以通过编译的 // 但实际情况是:编译期报错 Date d = new Date(); d.printDate(); } } /* Error:(26, 18) java: 无法将类 extend01.Date中的构造器 Date应用到给定类型; 需要: int,int,int 找到: 没有参数 原因: 实际参数列表和形式参数列表长度不同 */
2.3 构造方法中,可以通过this调用其他构造方法来简化代码
假设:在同一个类中,有两个构造方法,一个带参数,一个不带参数。那么我们可以通过使用 this 在一个构造方法中,调用另外一个构造方法 :
public class Date { int year; int month; int day; // 无参构造方法--内部给各个成员赋值初始值,该部分功能与三个参数的构造方法重复 // 此处可以在无参构造方法中通过this调用带有三个参数的构造方法 // 但是this(1900,1,1);必须是构造方法中第一条语句 public Date(){ //不带参数的都构造方法 this(1900, 1, 1); //this.year = 1900; //this.month = 1; //this.day = 1; } // 带有三个参数的构造方法 public Date(int year, int month, int day) { this.year = year; this.month = month; this.day = day; } }
【注意事项】
1. this(...)必须是构造方法中第一条语句,否则编译器会报错
2. 构造方法之间不能相互调用,形成环。比如:
public Date(){ this(1900,1,1); } public Date(int year, int month, int day) { this(); } /* 无参构造器调用三个参数的构造器,而三个参数构造器有调用无参的构造器,形成构造器的递归调用 编译报错:Error:(19, 12) java: 递归构造器调用 */
2.4 就地初始化
在声明成员变量时,就直接给出了初始值。在代码编译完成后,编译器会将所有给成员初始化的这些语句添加到各个构造函数中
public class Date { /* 在创建成员变量时,就对其进行初始化赋值 */ int year = 1900; int month = 1; int day = 1; public static void main(String[] args) { Date d1 = new Date(); System.out.println(d1.year+"年"+d1.month+"月"+d1.day+"日"); //输出:1900年1月1日 } }
但是,这种方式,只推荐应用在有特殊业务需求的时候。比如希望任何对象实例化后的成员变量的值都是一样的。
五. 封装 (private)
1. 封装的概念
面向对象程序三大特性:封装、继承、多态。而类和对象阶段,主要研究的就是封装特性。何为封装呢?
简单来说:封装 就是将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行 交互
直白的说法:
封装就是通过关键字 private 修饰成员变量 或 成员方法,被修饰的成员变量或方法,只能在当前类访问使用。
举例:
上面的举例,可以很清楚的看到,被封装的成员变量,是无法在当前类外引用的。成员方法也同理
那么,被封装的成员变量或者成员方法,如何在其他类进行调用呢?
提供一个可以访问的 “窗口”(get和set 方法)。
用上面代码的 year 变量举例:
为了更好的了解封装,我们首先要了解两个知识点,“包”是什么 和 访问限定符 (下面有详细介绍)
2. 包
2.1 包 的概念
1.在面向对象体系中,提出了一个软件包的概念,即:为了更好的管理类,把多个类收集在一起成为一组,称为软件包。
2.我们可以把 包 想象成 文件夹:不同功能的 类 放在不同的包下,这样就可以更好的管理和组织类(包 的重要作用)
3.包还有一个重要的作用:在同一个工程中允许存在相同名称的类,只要处在不同的包中即可。
2.2 导入包中的类
Java 中已经提供了很多现成的类供我们使用. 例如Date类:可以使用 java.util.Date 导入 java.util 这个包中的 Date类.
public class Test {
public static void main(String[] args) {
java.util.Date date = new java.util.Date();
// 得到一个毫秒级别的时间戳
System.out.println(date.getTime());
}
}
//但是这种写法比较麻烦一些, 可以使用 import语句导入包.
import java.util.Date;
public class Test {
public static void main(String[] args) {
Date date = new Date();
// 得到一个毫秒级别的时间戳
System.out.println(date.getTime());
}
}
/*
如果需要使用 java.util 中的其他类, 可以使用 import java.util.*( * 号 是通配符,可以理
解为代表所有的,即用需要那个类,它就代表那个类)
*/
对于像 import java.util.* 这种用法,更建议显式的指定要导入的类名. 否则可能出现冲突的情况.
比如:util包 和 sql包 中都存在一个 Date 这样的类, 此时就会出现歧义, 编译出错
假如,真的需要同时用到这两个包中名字相同的类。正确使用方法: 使用完整的类名
2.3 使用import static导入包中静态的方法和字段
import static java.lang.Math.*; public class Test { public static void main(String[] args) { double x = 30; double y = 40; // 静态导入的方式写起来更方便一些. // double result = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); double result = sqrt(pow(x, 2) + pow(y, 2)); System.out.println(result); } }
【注意事项】
import 和 C++ 的 #include 差别很大。C++ 必须 #include 来引入其他文件的所有内容, 但是 Java 不需要,即使import 导入的包使用了通配符 * ,这个包中有很多的类。但是,在程序编译时,只有用了那个类,才会导入那个类。import 只是为了写代码的时候更方便.。import 更类似于 C++ 的 namespace 和 using。
2.4 自定义包
基本规则:
- 在文件的最上方加上一个 package 语句指定该代码在哪个包中.
- 包名需要尽量指定成唯一的名字(小写), 通常会用公司的域名的颠倒形式(例如 com.bit.demo1 ).
- 包名要和代码路径相匹配. 例如创建 com.bit.demo1 的包, 那么会存在一个对应的路径 com/bit/demo1 来存储代码.
- 如果一个类没有 package 语句, 则该类被放到一个默认包中.
3. 访问限定符
Java中主要通过类和访问权限来实现封装:类可以将数据以及封装数据的方法结合在一起,更符合人类对事物的认
知,而访问权限用来控制方法或者字段能否直接在类外使用。Java中提供了四种访问限定符:
private | default(默认权限) | protected | public | |
同一包下的同一类 | 可以访问 | 可以访问 | 可以访问 | 可以访问 |
同一包下的不同类 | 可以访问 | 可以访问 | 可以访问 | |
不同包中的子类 | 可以访问 | 可以访问 | ||
不同包中的非子类 | 可以访问 |
【注意事项】
1. default权限指:什么都不写时的默认权限
2. 访问权限除了可以限定类中成员的可见性,也可以控制类的可见性
总结:
1. 封装,就是利用访问限定符 private 去限制被修饰的成员变量或方法 的使用范围。这样有利于程序的安全性。
2. 包,就相当于对 类 的分类,使程序员能更好的组织管理 类。
3. 访问限定符就是用来修饰数据的可访问范围。
六. static成员
在Java中,被static修饰的成员,称之为静态成员,也可以称为类成员,其不属于某个具体的对象,是所有对象所共享的
1. static修饰成员变量
static修饰的成员变量,称为静态成员变量,静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共享的
【静态成员变量特性】
1. 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
2. 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问
3. 类变量存储在方法区当中
4. 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)
例如:有一个学生类:有很多对象,每个对象的名字,性别不同,但是他们所在的班级相同,所以,可以把班级单独拿出来,作为静态成员 变量
class Student{
public String name;
public String sex;
static String clas="五班";
public Student(String name,String sex){
this.name=name;
this.sex=sex;
}
}
public class Demo {
public static void main(String[] args) {
Student st=new Student("张三","男");
Student st2=new Student("李四","男");
System.out.println(st.name+" "+st.sex+" 在"+Student.clas); //输出:张三 男 在五班
System.out.println(st2.name+" "+st2.sex+" 在"+Student.clas); //输出: 李四 男 在五班
}
}
2.static修饰成员方法
Java中,被static修饰的成员方法称为静态成员方法,是类的方法,不是某个对象所特有的。它同静态变量一样,在不同类中,可以通过
类名. 的方式直接引用。在同一类下,可直接通过方法名调用。
注:在Java中,若静态成员变量被 private修饰,则在一般是通过静态方法来访问的。比如:
public class Student{
private static String classRoom = "五班";
public static String getClassRoom(){
return classRoom;
}
}
public class TestStudent {
public static void main(String[] args) {
System.out.println(Student.getClassRoom()); //输出:五班
}
}
【静态方法特性】
1. 不属于某个具体的对象,是类方法
2. 可以通过对象调用,也可以通过类名.静态方法名(...)方式调用,更推荐使用后者
3. 不能在静态方法中访问任何非静态成员变量
4. 静态方法中不能调用任何非静态方法,因为非静态方法有this参数,在静态方法中调用时候无法传递this引用5. 静态方法无法重写,不能用来实现多态
3. static成员变量的初始化:
分为两种:就地初始化(定义的时候直接赋值)
静态代码块初始化
七. 代码块
使用 {} 定义的一段代码称为代码块。
1. 普通代码块
定义在方法中的代码块(使用较少):
public class Main{
public static void main(String[] args) {
{ //直接使用{}定义,普通方法块
int x = 10 ;
System.out.println("x1 = " +x);
}
int x = 100 ;
System.out.println("x2 = " +x);
}
}
/*
执行结果
x1 = 10
x2 = 100
*/
2. 构造代码块
构造块:定义在类中的代码块(不加修饰符)。也叫:实例代码块。构造代码块一般用于初始化实例成员变量。
public class Student{
//实例成员变量
private String name;
private String gender;
private int age;
private double score;
public Student() {
System.out.println("构造方法");
}
//实例代码块
{
this.name = "张三";
this.age = 18;
this.sex = "男";
System.out.println("构造代码块");
}
public void show(){
System.out.println("name: "+name+" age: "+age+" sex: "+sex);
}
}
public class Main {
public static void main(String[] args) {
Student stu = new Student();
stu.show();
}
}
/*
运行结果
构造方法
构造代码块
name: 张三 age: 18 sex: 男
*/
3.静态代码块
使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量。
class Student{
public static String name;
public static String sex;
public static String clas;
//静态代码块
static {
name="王二麻子";
sex="男";
clas="五班";
}
}
public class Demo {
public static void main(String[] args) {
System.out.println(Student.name);
System.out.println(Student.sex);
System.out.println(Student.clas);
/* 输出结果:
王二麻子
男
五班
*/
}
}
【注意事项】
1. 静态代码块不管生成多少个对象,其只会执行一次
2. 静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
3. 如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行(合并)
4. 实例代码块只有在创建对象时才会执行