定义
- OOP,面向对象编程
- 类,成员属性、成员方法、构造器;
- 三大特征:封装、继承、多态;
- 关键字,this,super,static,final, abstract, interface, import, package
- OOD,面向对象设计
- 面向过程,强调功能实现的顺序,以函数为最小单位,如C语言;
- 面向对象,强调功能实现的主题,以类/对象为最小单位,如java/python;
抽象数据类型,将不同的数据类型封装在一起,来描述一个新的事物。
类,对一类事物共同特征的抽象,是一种抽象的数据类型;面向对象编程中需要先定义(抽象)出类;
对象,一类事物中具体的一个实例;
类、接口、数组、字符串是引用数据类型,使用new创建,如String就是引用类型,引用类型的默认值为null。
定义一个类
- 类定义在一个.java类文件中;
- 一个.java文件中只能有一个public类,且public类名与文件名相同;
- 包含主函数的主类调用其他类,同一个包中的类可以直接使用,不同包中的类需要import导入;
- 创建项目,这里使用idea开发工具。
创建好的项目目录:
在src目录上,右键>new>package 创建一个包,名字自定义;
在创建的包上,右键>new>java class 创建源文件xxx.java;
- 在.java文件中编写代码
// 创建一个学生类 Stu.java
public class Stu {
// 成员属性
String name = 'jack';
int age;
char sex;
// 成员方法
public void run(){
System.out.println(name + "跑步中..."); // 只能输出一个字符串,必须拼接
}
}
- 在主类的主方法中调用 学生类
import School.Stu; //导入(其他包)的类文件,然后就可以切入到其内部的public类
//主类
public class Main {
//主函数
public static void main(String[] args) {
// 创建学生对象
Stu stu = new Stu();
// 成员属性都有默认值,int 0 float 0.0 boolean false char 0 引用类型null
// 方法形参、方法内部变量为局部变量,没有默认初始化,必须先赋值,才可以使用
// 成员变量(非static)随对象一起存入堆内存;而局部变量存入栈;
//调用 成员方法
stu.run();
}
}
在主类代码中,右键run 即可
-
主类中的代码分析
public,公共访问权限,主方法是一切程序的开始点,必须是公共的
static,静态方法,类与实例对象均可以调用。
void,无返回值
main,固定的主方法,程序入口
String[] args,字符串数组,程序启动时,接收参数; -
当前目录结构
Main是主类的源文件;
同一个包(文件夹)中的类之间可以相互引用,不用import导入;
不同的包之间,就需要导入且只能引用public类(全局类);
非public类(保留类)只能本包内部使用;
内存解析
- 对象、数组使用new创建,存入堆内存中;
- 虚拟机栈,存储局部变量;
- 方法区,存储已被虚拟机加载的类、常量、static静态变量;
- 案例解析:
- new一个对象存入堆;
- main中声明变量赋值,即局部变量引用对象;
构造方法
构造函数,用于创建对象时,初始化对象的属性值。
类似于python的__init__方法
- 必须与类名相同;
- 无返回值
- 创建对象时,自动调用构造函数
- 不写构造函数时,编译器提供一个无参构造函数;
- 写构造函数,编译器不再提供;可以重载;
public class Stu{
int name;
int age;
public Stu(){
//构造函数
this.name = "jack";
this.age = 23; // this代表当前对象
}
//重载构造函数
public Stu(String name, int age){
this.name = name;
this.age = age;
}
}
普通方法
类中就两种方法
- 构造方法
- 普通方法
- 权限修饰符 返回类型 函数名(形参){ 函数体 };
- 权限修饰符
- public,公有,最大访问权限;
- protected, 保护,访问受限;
- private,私有,只能在类内部访问;
- 缺省,
- 返回值
- void,无返回值,可以使用return结束;或者不写return;
- int/float/double/String… 有返回值;函数体需要return对应类型;
- 方法名,小驼峰+可读性
- 形参,类型 + 形参名;定义时形参可有可无;
- 方法体内部可以直接调用当前类的成员属性、成员方法(对象有则优先调用对象的);方法中不能再定义方法;
- 静态方法内部只能调用静态的成员属性、成员方法
import java.util.Scanner;
public class Main{
public static void main(String[] args){
User.run();
User user = new User("jack", 55);
System.out.println(user.getAge());
}
}
// 非public类
class User {
static String name, password = "123";
int age = 25;
// 构造方法,初始化对象
public User(String name, int age){ // 不指定返回类型
// 初始化
this.name = name; // 属性必须在成员属性中定义出来
this.age = age;
System.out.println("构造函数被调用");
}
public static String getName(){
return name;
}
public void walk(){
System.out.println("i am walking." + password); // 对象没有 则调用类的password
}
public int getAge(){
walk(); // 调用成员方法
return age; // 调用成员属性(对象有则调用对象的,否则调用类的)
}
public static void run(){
// 方法内部 可以调用类的属性、方法
// 静态方法内部 只能调用类的静态属性
System.out.println(name);
// 静态方法内部,只能调用类的静态方法;
getName();
}
}
可变个数形参
- jdk5.0新特性;
- 参数格式:类型…变量名,收集参数放入数组,必须在所有参数的最后位置;
- 自动匹配不确定个数的实参,将同类型的多个参数收集到数组中;
- 函数名相同,与同类型的数组参数不能构成重载;
package School;
public class CollectArgs {
public static void main(String[] args) {
CollectArgs ca = new CollectArgs();
ca.collectName("jack");
ca.collectName(34, "tom", "lucy", "lili"); // 收集参数
}
// 单个参数
public void collectName(String name){
System.out.println(name + "\n");
}
// 收集多个参数,存入数组
public void collectName(int numId, String...nameArr){
System.out.println(numId);
for (String s : nameArr) { // 强化的for
System.out.print(s + " ");
}
}
// 不与同类型的数组 构成重载
public void collectName(int numId, String[] arr){}
}
方法参数的传递
-
基本数据类型去赋值给一个变量,存入数值;对象、接口、数组、字符串等引用类型赋值,存入地址(引用);
-
方法参数,(基本数据类型)值传递
-
方法参数,(引用类型)地址传递
-
案例1
package School;
public class Case1 {
public static void main(String[] args) {
int a = 10;
int b = 20;
method(a, b); // 静态方法
// 调用method后,输出a=100, b=200
System.out.println("a=" + a);
System.out.println("b=" + b);
}
public static void method(int a, int b){
a *= 10;
b *= 10;
System.out.println("a=" + a);
System.out.println("b=" + b);
System.exit(0); // 进程退出 防止在外部打印
}
}
方法的重载
- 方法的签名,函数名+参数类型列表;一个类中
不允许
签名一样; - 方法的重载overload,同一个类中同函数名,但参数类型列表(包括顺序)不同;
- 类型不同
- 顺序不同;
- 个数不同;
- 与权限修饰符、返回值、参数名称、函数体无关;
- 根据参数类型,选择调用不同的函数体;
public class Cell{
int row;
int col;
void moveDown(){ // 同一个类,同名函数,不同参数类型/参数长度;
row++;
}
// 方法的重载(参数不同)
void moveDown(int n){
row += n;
}
}
this关键字
对象调用函数时,函数内部的this代表当前对象;内部使用的属性都是(this.)attr,前面有个this. ,表示当前对象的属性。
this() 调用构造函数,只能在构造函数内部使用。
public class Stu{
int name;
int age;
public Stu(){
//构造函数
this("jack", 23); //调用构造函数
}
//重载构造函数
public Stu(String name, int age){
this.name = name;
this.age = age;
}
void setName(String name){
// this.表示当前对象 可以省略
this.name = name;
}
}
核心关键字
public,公有属性/方法,作用域范围内谁可以访问;
private,私有属性/方法,只在类内部使用,对象只能通过方法来操作私有属性;
protected,保护的属性/方法,只有本包中的类才能调用;
static,静态的属性是全局公共属性,保存在全局数据区;
静态的方法,不能使用this关键字;
静态的属性/方法,类与对象均可以调用。
同main方法并列的方法用static
final 定义类,不可以被继承
final 定义方法,不可以被覆盖(重写)
final 定义变量,为常量
内存管理
JVM的内存管理:
- 堆 存储new创建的对象
- 栈 存储临时变量
- 方法区
基本数据类型,分配内存并赋值,互不影响。
c = null 无引用 空指针异常
案例
- 面向对象 思路总结
- 确定需要用到哪些对象
- 抽象出类(属性、方法)
- 定义类
- 实例化对象
- 定义一个Stu类,内部实现main方法、addAge方法、构造方法;
- 构造方法,初始化对象的姓名、年龄;
- addAge方法,实现年龄+n, n为参数;
- main方法内部调用addAge方法;
- 静态方法内部,只能访问静态的成员属性、静态的成员方法;非静态的使用实例调用
// 创建Stu.java类文件
// 类文件中只能有一个public 类,且类名与文件名相同
public class Stu { // 全局类 可以导入到任何类文件中
// 成员属性(必须先定义,对象才可以使用)
static String name = "Stu class"; // 静态的成员变量在方法区,只有一份
int age;
// 构造方法,初始化对象
public Stu(String name, int age){
// this为当前对象
this.name = name;
this.age = age;
}
public void addAge(int n) {
age += n; // 对象调用,则表示实例对象的age属性 (谁调用 就是谁的属性)
}
// static方法
public static void getName(){
System.out.println(name);
}
// main
public static void main(String[] args){
// static静态方法内部,只能访问 静态的成员属性、静态的成员方法
Stu stu = new Stu("jack", 23); // 非静态,则使用对象调用
stu.addAge(5);
System.out.println("学生年龄:" + stu.age);
// 直接调用静态方法
getName();
Stu stu2 = new Stu("tom", 18);
getName();
}
}
- 定义一个Stu类,成员属性为stuId, grade, score, 均为int类型;
- 创建一个StuTest类,main方法中创建一个长度为10的Stu数组;
- stuId 为1-10之间;
- grade 为1-5,采用Math.random() 0-1之间的随机数;
- score 为0-100,采用Math.random()随机,Math.round()四舍五入;
- getStuInfo(Stu[] arr, int grade) 获取指定年级的学生信息;
- bubbleSort(Stu[ ] arr) 按照分数冒泡升序排序
// School 包
// 创建Stu.java
package School;
public class Stu {
// 成员变量
int stuId, grade, score;
// 构造方法初始化
public Stu(int stuId, int grade, int score){
this.stuId = stuId;
this.grade = grade;
this.score = score;
}
}
// 创建StuTest.java
package School; // 声明 所在包
public class StuTest{
public static void main(String[] args) {
// 学生数组
Stu[] arr = new Stu[10];
for(int i=0; i<arr.length; i++){
int stuId = i + 1;
// Math.random() 0-1之间的随机数
// Math.round() 四舍五入 返回long, 需要强制类型转换
int grade = (int) Math.round(Math.random() * (5 - 1 + 1)) + 1;
int score = (int) Math.round(Math.random() * (100 - 0 + 1));
arr[i] = new Stu(stuId, grade, score);
}
// 获取4年级的学生信息
getStuInfo(arr, 4);
// 按照学生成绩升序排序
bubbleSort(arr);
}
public static void getStuInfo(Stu[] arr, int grade){
for(int i = 0; i < arr.length; i++){
if(arr[i].grade == grade){
printStuInfo(arr[i]);
}
}
}
public static void printStuInfo(Stu stu){
System.out.println("学号:" + stu.stuId + "\n" + "年级:" +
stu.grade + "\n" + "分数:" + stu.score + "\n");
}
// 非静态时,使用对象调用
public static void bubbleSort(Stu[] arr){
for(int i=0; i<arr.length-1; i++){
for(int j=0; j<arr.length-1-i; j++){
if(arr[j].score > arr[j+1].score){
Stu temp = arr[j+1];
arr[j+1] = arr[j];
arr[j] = temp;
}
}
}
// 数组为引用类型;
for(int k=0; k<arr.length; k++){
System.out.println(arr[k].score);
}
}
}
- 控制俄罗斯方块水平位置、垂直位置
package School;
public class BlockGame {
//本包 以外调用,必须是public
public Cell cell = new Cell();
//静态方法
public static String testPosition(Cell cell){
return cell.getPosition();
}
public void moveDown(Cell cell){
cell.moveDown();
}
}
class Cell{ //非public的保留类,只能包内部使用
//行数 0 1 .. 39
int row; // 初始化时为0
int col;
// 向下移动
public void moveDown(){
if(row < 39){
row++;
}
}
//向左移动
void moveLeft(){
if(col > 0){
col--;
}
}
void moveRight(){
if(col < 19){
col++;
}
}
String getPosition(){
return row + ", " + col;
}
}
必须是public
的属性、方法才可以跨包使用。
非public的保留类,只能在本包内部使用,即使跨包拿到了保留类的对象,也无法跨包操作对象的属性、方法。