文章目录
- 一、类和对象的基础概念
- 1.1 类?对象?面向对象?
- 1.2 创建类 和 实例化对象
- 1.3 识别类
- 1.4 对象的打印
- 1.5 类和类之间的关系
- 二、进阶概念
- 2.1 方法参数
- 2.2 this 引用
- 2.3 构造方法
- 2.4 初始化
- 2.5 包
- 2.6 封装
- (1)概念
- (2)如何封装 ------ >访问权限
- 2.7 static
- 2.8 代码块
一、类和对象的基础概念
1.1 类?对象?面向对象?
- 类
类( class) 是构造对象的模板或蓝图,由类构造(construct) 对象的过程称为创建类的实例(instance ) - 面向对象
- 面向对象指不关注如何去完成这件事的过程,而是注重与对象与对象之间的交互。
面向过程:如我要去买手机,我需要了解这个手机的性能,比较各个平台上的价格,下单时填好地址,然后去拿快递……
面向对象:我只要告诉一个人,让他帮我去买个什么样的手机,至于如何去买,会遇到什么困难,那是他的事情
面向对象的程序是由对象组成的, 每个对象包含对用户公开的特定功能部分和隐藏的实现部分
1.2 创建类 和 实例化对象
创建类
class ClassName{
field; //属性/字段/成员属性/成员变量
method;}
- 一般来说,一个文件一个类, 一个类对应一个class文件
- 如果要使用不同源文件中的类,需要导包
- 类名是大驼峰写法
- main方法所在的类一般要使用public修饰(注意:Eclipse默认会在public修饰的类中找main方法)
- public修饰的类必须要和文件名相同
实例化对象
- 通过 new 创建一个新的对象(new操作符返回的是一个引用)
- 一个类可以实例化出多个对象
- 通过.去访问这个类中的属性/方法
- 这个对象根据类创建后,里面会有类设置好的属性,方法的调用需要JVM
- 在 Java 中,任何对象变量的值都是对存储在另外一个地方的一个对象的引用
- 所有的 Java 对象都存储在堆中。 当一个对象包含另一个对象变量时, 这个变量依然包含着指向另一个堆对象的指针
class WashMachine{
public String name;
public int price;
public void setTime(){
System.out.println("设置时间");
}
}
public class Main {
public static void main(String[] args) {
//这个是定义引用变量 //这个是实例化对象
WashMachine washMachine = new WashMachine();
washMachine.name = "小天鹅";
WashMachine = null; //表明这个引用变量目前没有引用任何对象,
//如果将一个方法应用于一个值为 null 的对象上,那么就会产生运行时错误
}
}
1.3 识别类
首先从设计类开始,然后再往每个类中添加方法。
识别类的简单规则是在分析问题的过程中寻找名词,而方法对应着动词
例如, 在订单处理系统中,有这样一些名词
•商品(Item)
•订单(Order)
•送货地址(Shippingaddress)
•付 款 ( Payment )
•账户(Account)
这些名词很可能成为类 Item、 Order 等。
接下来, 查看动词:商品被添加到订单中, 订单被发送或取消, 订单货款被支付。对于每一个动词如:“ 添加”、“ 发送”、“ 取消” 以及“ 支付”, 都要标识出主要负责完成相应动作的对象。例如,当一个新的商品添加到订单中时, 那个订单对象就是被指定的对象, 因为它知道如何存储商品以及如何对商品进行排序。也就是说,add 应该是 Order 类的一个方法,而 Item 对象是一个参数
1.4 对象的打印
class Stu{
private String name;
public int age = 20;
public Stu(String name, int age) {
this.name = name;
this.age = age;
}
public void print(){
System.out.println("姓名:" + this.name + " 年龄 " + this.age);
}
}
public class Main{
public static void main(String[] args) {
Stu stu = new Stu("aaaa",19);
stu.print();
}
}
但是当我们仔细研究这个println方法后,发现他最后调用的是一个toString方法,那我们就可以通过方法的重写,快速实现对象的打印
class Stu{
private String name;
public int age = 20;
public Stu(String name, int age) {
this.name = name;
this.age = age;
}
public String toString(){
return "姓名:" + this.name + " 年龄 " + this.age;
}
}
public class Main{
public static void main(String[] args) {
Stu stu = new Stu("aaaa",19);
System.out.println(stu);
}
}
但是这样仍然写,IDEA给我们提供了快捷键(鼠标右键 + Generate + toString)
class Stu{
private String name;
public int age = 20;
public Stu(String name, int age) {
this.name = name;
this.age = age;
}
@Override //注解
public String toString() {
return "Stu{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Main{
public static void main(String[] args) {
Stu stu = new Stu("aaaa",19);
System.out.println(stu);
}
}
1.5 类和类之间的关系
- 依赖(“ uses-a”)
- 如果一个类的方法操纵另一个 类的对象,我们就说一个类依赖于另一个类
- 应该尽可能地将相互依赖的类减至最少
- 聚合(“ has-a”)
- 例如, 一个Order 对象包含一些 Item 对象。聚合关系意味着类 A 的对象包含类 B 的对象
- 继承(“ is-a”)
二、进阶概念
2.1 方法参数
- 一个方法不能修改一个基本数据类型的参数(即数值型或布尔型)
- 一个方法可以改变一个对象参数的状态
- 一个方法不能让对象参数引用一个新的对象
2.2 this 引用
1.前情提要
class Date{
public int year;
public int month;
public int day;
public void setDate(int year, int month, int day){
year = year;
month = month;
day = day;
}
public void printf(){
System.out.println("年:" + year + " 月:" + month + " 日:" + day);
}
}
public class Main{
public static void main(String[] args) {
Date date1 = new Date();
Date date2 = new Date();
Date date3 = new Date();
date1.setDate(2022,3,1);
date2.setDate(2023,4,6);
date3.setDate(2060,9,9);
date1.printf();
date2.printf();
date3.printf();
}
}
我想实现的是把传过去的这三个日期,赋给创建好的对象date1、date2、date3,然后再打印,可是打印的结果却如下图
解析
(1)setDate内部并不知道这个year、month、day指的是哪个year、month、day,这里是根据局部变量优先的原则,即第一个year是成员变量
(2)成员方法内部没有描写具体传过来对象的标识
2.解决方法
class Date{
public int year;
public int month;
public int day;
public void setDate(int year, int month, int day){
this.year = year;
this.month = month;
this.day = day;
}
public void printf(){
System.out.println("年:" + this.year + " 月:" + this.month + " 日:" + this.day);
}
}
public class Main{
public static void main(String[] args) {
Date date1 = new Date();
Date date2 = new Date();
Date date3 = new Date();
date1.setDate(2022,3,1);
date2.setDate(2023,4,6);
date3.setDate(2060,9,9);
date1.printf();
date2.printf();
date3.printf();
}
}
概念:this 指的是成员方法运行时调用该成员方法的对象
特性:
(1)this 只能在静态成员方法中使用
(2) this是“成员方法”第一个隐藏的参数,编译器会自动传递,在成员方法执行时,编译器会负责将调用成员方法
(3)在某些情况下,不加上this 也可能可以正常编译通过,但是最好加上this,一定不会错
3.通过this来调用构造方法
class Stu{
public String name;
int age;
public Stu() {
this("wxy", 19);
System.out.println("这是没有参数的构造方法");
}
public Stu(String name, int age) {
this.name = name;
this.age = age;
System.out.println("这是有两个参数的构造方法");
}
public void read(){
System.out.println(this.name + "在看书");
}
public void print(){
System.out.println("名字: " + this.name + " 年龄:" + this.age);
}
}
public class Main{
public static void main(String[] args) {
Stu stu = new Stu();
stu.print();
}
}
- 注意点
- this 只能在构造方法中使用
- 必须在构造方法中的第一行
- 不能自己调用自己
2.3 构造方法
- 概念:构造方法在形式上是没有返回值的方法,方法名必须和类名一致,用来初始化对象
- 作用:构造对象和初始化对象的成员变量
- 特点:
- 可以有多个构造方法,或者说构造方法可以方法重载
- 构造方法是在new创建这个对象的时候,调用的
- 成员变量在没有主观初始化的时候,也能使用,是因为如果程序员没有写构造方法,编译器会自动生成一个无参数,方法体内容为无的一个构造方法(将所有成员变量设为默认值)。但是注意,如果程序员写了一个及以上的构造方法,编译器便不会自动生成
- IDEA快速生成:鼠标右键 + 点击Generate + Constructor(选中几个就生成带几个参数的构造方法)
2.4 初始化
1.在构造器中设置值
2.在声明中赋值
3.初始化块
1.默认初始化
成员变量在没有初始化的时候,会默认有个初始值
数据类型 | 默认值 |
byte | 0 |
char | '\u0000' |
short | 0 |
int | 0 |
long | 0L |
boolean | false |
float | 0.0f |
doubke | 0.0 |
reference | null |
2.就地初始化
class Stu{
public String name = "wxy";
int age = 19;
public void read(){
System.out.println(this.name + "在看书");
}
public void print(){
System.out.println("名字: " + this.name + " 年龄:" + this.age);
}
}
public class Main{
public static void main(String[] args) {
Stu stu = new Stu();
stu.print();
}
}
3.初始化块
首先运行初始化块,然后才运行构造器的主体部分
4.总结
- 所有数据域被初始化为默认值(0、false 或 null。)
- 按照在类声明中出现的次序, 依次执行所有域初始化语句和初始化块
- 如果构造器第一行调用了第二个构造器,则执行第二个构造器主体
- 执行这个构造器的主体
2.5 包
1.什么是包
就像是计算机上的文件夹一样,将一个个文件装起来,使得界面更加整洁
在Java中,包是用来组织类/接口的一种组织形式。同时,在不同包中,可以有相同名字的类
2.如何导包
- 使用import导包
import java.util.Scanner;
- 暴力导包
`java.util.Scanner scanner = new java.util.Scanner(System.in);
- 通配符导包
1.*是通配符,可以充当任何类,不是导入util下的所有类,而是作用到哪个类,它帮你导哪个类
2.但是因为不同类可能有相同名字的类,会造成无法识别,所以非必要不要使用
3.只能使用星号(* ) 导入一个包, 而不能使用 import java.* 或import java.. 导入以 java 为前缀的所有包
/*import java.util.Arrays;
import java.util.Scanner;*/
import java.util.*; //这个可以抵得上上面两行
public class Main{
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int[]arr = {3,1,9,5,27};
Arrays.sort(arr);
}
}
- 静态导包
import 语句不仅可以导入类,还增加了导入静态方法和静态域的功能
1.例如,如果在源文件的顶部, 添加一条指令:
import static java.lang.System.*;
就可以使用 System 类的静态方法和静态域,而不必加类名前缀:
out.println(“Goodbye, World!”); // i.e., System.out
exit⑼; //i.e., System.exit
2.另外,还可以导入特定的方法或域:import static java.lang.System.out;
这种编写形式不利于代码的清晰度。不过,sqrt(pow(x, 2) + pow(y, 2))看起来比Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))清晰得多
3.将类导入包
要想将一个类放人包中, 就必须将包的名字放在源文件的开头,包中定义类的代码之前
4.如何查看Java中的包
5.自定义包
- 基本规则
- 在文件的最上方加上一个 package 语句指定该代码在哪个包中.
- 包名需要尽量指定成唯一的名字, 通常会用公司的域名的颠倒形式(例如 com.bit.demo1 ).
- 包名要和代码路径相匹配. 例如创建 com.bit.demo1 的包, 那么会存在一个对应的路径 com/bit/demo1 来存储代码.
- 如果一个类没有 package 语句, 则该类被放到一个默认包( defaulf package ) 中,默认包是一个没有名字的包,这个包里面的类/方法无法调用。
2.6 封装
(1)概念
对于计算机而言,用户只需要会使用鼠标、键盘、软件等一些操作就好,至于计算机的一些硬件组成,是被壳子包裹起来的,封装就类似于这个壳子的效果
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互
实现封装的关键在于绝对不能让类中的方法直接地访问其他类的对象的数据。程序仅通过对象的方法与对象数据进行交互,这意味着一个类可以全面地改变存储数据的方式,只要仍旧使用同样的方法操作数据, 其他对象就不会知道或介意所发生的变化。
(2)如何封装 ------ >访问权限
访问权限用来控制方法或者字段能否直接在类外使用
编号 | 范围 | private | default(默认权限) | protected | public |
1 | 同一个包中的同一类 | √ | √ | √ | √ |
2 | 同一包中的不同类 | √ | √ | √ | |
3 | 不同包中的子类 | √ | √ | ||
4 | 不同包中的非子类 | √ |
2.7 static
1.前情提要
如果我要定义所有二班的对象,那么其实除了姓名、学号等特殊证明外,这些同学总是有一些相同点的,如他们都在同一个教室,但是给每个同学都赋一个相同的值,实在是太麻烦了。
这时候,我们就希望这个教室只存在一个,所有二班的同学都在这个教室里面,这时候就可以用static修饰了
在Java中,被static修饰的成员,称之为静态成员,也可以称为类成员,其不属于某个具体的对象,是所有对象所共享的。
static 存在于方法区
静态变量可以认为在加载的时候初始化了,也是最先被执行的
2.static修饰成员变量
如果将域定义为 static, 每个类中只有一个这样的域。而每一个对象对于所有的实例域却都有自己的一份拷贝
只有一个静态域 classRoom。即使没有一个对象, 静态域 classRoom 也存在。它属于类,而不属于任何独立的对象
class Stu{
private String name;
private int age;
public static String classRoom = "301";
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
}
public class Test {
public static void main(String[] args) {
System.out.println(Stu.classRoom);
}
}
ps.如果是静态成员常量,最好设为final,不然容易被外面修改
2.static修饰成员方法
使用方式于static 修饰成员变量相似,也是不依赖于对象的,但是有一些注意点
- 静态方法的内部,是不能调用非静态的方法的,因为静态方法依赖对象
- 静态方法中不能使用this,因为静态方法依赖对象(静态方法是一种不能向对象操作的方法)
3.静态成员的访问方式
- 成员名.
- 可以访问,但不推荐,因为此时的静态成员变量并不是属于对象的(不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中),而且容易与非静态的访问方式冲突,分不清是静态的还是非静态的
- 类名.
4.main方法
需要注意,不需要使用对象调用静态方法
每一个类可以有一个 main 方法。这是一个常用于对类进行单元测试的技巧
2.8 代码块
代码块就是用{ }包起来的
- 静态代码块
- 定义在类里面,方法的外面,用来初始化静态成员变量
- 在调用静态成员/静态成员方法时调用
- 不管执行多少次,只会生成一次,因为类只会加载一次
- 如果一个类中包括多个静态代码块,在编译代码时,编译器会按照定义的先后顺序依次合并
- 实例 / 构造代码块
- 定义在类里面,方法的外面,用来初始化成员变量
- 在new出对象时调用、
❤️顺序问题:
静态代码块 > 构造代码块 > 构造方法
ps.如果多个静态或者多个构造的同时存在,则看代码的顺序来执行
class Stu{
private String name;
private int age;
{
System.out.println("这是构造代码块");
}
public Stu(String name, int age) {
this.name = name;
this.age = age;
System.out.println("这是构造方法");
}
}
public class Main{
public static void main(String[] args) {
Stu stu = new Stu("aaaa",19);
}
}
- 普通代码块
- 只是简单的用{}括起来,在括号里面的变量的作用域就是在那个括号里面
- 同步代码块