Java学习之类和对象、内存底层
目录
表格结构和类结构
表格的动作和类的方法
与面向过程的区别
具体实现
对象和类的详解
类的定义
属性(field 成员变量)
方法
示例--编写简单的学生类
简单内存分析(理解面向对象)
构造方法(构造器 constructor)
声明格式:
四个要点
练习
构造方法的重载
测试
JVM虚拟机内存模型
参数传值机制
基本数据类型参数的传值
引用类型参数的传值
示例
垃圾回收机制(Garbage Collection)
原理和算法
通用的分代垃圾回收机制
JVM调优和Full GC
容易造成内存泄漏的操作
this关键字
用法
static关键字
特点
静态初始化块
注意事项
变量的分类和作用域
包机制(package、import)
两个要点
JDK中的主要包
导入类import
注意点
静态导入
使用
表格结构和类结构
面向对象中,类对应表的结构(表的field)
表格的动作和类的方法
动作---定义成方法
对象对应“表中的数据”
与面向过程的区别
-
都是对软件分析、设计和开发的一种思想,C语言是一种典型的面向过程语言,Java是一种典型的面向对象语言。
-
面向过程适合简单、不需要协作的事务,重点关注如何执行,是一种执行思维
-
面向对象更契合人的思维模式,是一种设计者思维
面向对象可以帮助我们从宏观上把握,从整体上分析整个系统。但具体到微观操作,仍然需要面向过程的思路,它们搜索相辅相成的
具体实现
设计时,先从问题中找名词,确定哪些名词可以作为类,再根据问题需求确定类的属性和方法,确定类之间的关系。
对象和类的详解
类:class,对象:Object、instance(实例)。
-
类可以看作一类对象的模板,对象可以看作该类的一个具体实例
-
类用于描述同一类型的对象的一个抽象概念,定义了这一类对象所应具有的共同属性、方法
类的定义
每个源文件必须有且仅有一个public class,并且类名与文件名保持一致
一个Java文件可以定义多个class
对于一个类来说,有三种成员:
-
属性(field)
-
方法(method)
-
构造器(constructor)
属性(field 成员变量)
属性用于定义该类或该类对象包含的数据或静态特征,作用范围是整个类体,定义成员变量时可对其初始化,如果不初始化,Java默认做初始化。
成员变量的默认值 | 默认值 |
---|---|
数据类型 | 默认值 |
整型 | 0 |
浮点型 | 0.0 |
字符型 | '\u0000' |
布尔型 | flase |
所有引用类型 | null |
定义格式:
[修饰符] 属性类型 属性名 = [默认值] ;
方法
用于定义该类或该类实例的行为特征和功能实现。是类和对象行为特征的抽象。
整个程序最基本的单位是类,方法从属于类和对象。
格式:
[修饰符] 方法返回值类型 方法名(形参列表) {
// n条语句
}
示例--编写简单的学生类
public class student {int id;int age;String name; public void study(){System.out.println("学习呢!");} public void play(){System.out.println("好玩爱玩!");} public static void main(String[] args) {student s = new student(); // 创建了一个对象System.out.println(s.id);System.out.println(s.name);System.out.println(s.age);s.name = "张三";s.age = 18;System.out.println(s.name);System.out.println(s.age); s.play();s.study();} }
简单内存分析(理解面向对象)
栈(stack):main()的栈帧:args:null、s:堆中的值
堆(heep):id、age、name(属性--都是默认值),study()、play()两个方法
方法区:student类的信息:study()、play()代码;常量池;static属性和方法
创建对象四步:
-
分配对象空间,并将对象成员变量初始化为0或空
-
执行属性值的显式初始化
-
执行构造方法
-
返回对象的地址给相关的变量
构造方法(构造器 constructor)
用于对象初始化,不是创建对象
声明格式:
[修饰符] 类名(形参列表){
// n条语句
}
四个要点
-
构造器通过关键字new调用
-
构造器虽然有返回值,但不能定义返回值类型(返回值的类型肯定不是本类),不能在构造器里使用return返回某个值
-
如果没有定义构造器,编译器会自动定义一个无参的构造方式。
-
构造器的方法名必须和类名一致
练习
定义一个"点"(Point)类来表示二维空间中的点(有两个坐标),要求:
-
可以生产具有特定坐标的点对象
-
提供可计算该"点"距另外一点距离的方法
public class Point {double x,y; Point(double _x,double _y){x = _x;y = _y;} public double getDistance(Point p){return Math.sqrt((x-p.x) * (x-p.x) + (y-p.y) * (y- p.y));} public static void main(String[] args) {Point p1 = new Point(3.0,4.0);Point origin = new Point(0.0,0.0);System.out.println(p1.getDistance(origin));} }
构造方法的重载
测试
public class user {int age;int id;String name; public user(){}public user(int id){this.id = id;} public user(int id,String name){this.id = id;this.name = name;} public user(int age, int id, String name) {this.age = age;this.id = id;this.name = name;} public static void main(String[] args) {user a = new user(1);user b = new user(2,"张三");user c = new user(18,3,"李四");} }
快捷键生成构造器:右键--generate--constructor
JVM虚拟机内存模型
学习JVM虚拟机内存模型能够更好的理解面向对象
分为三个区域:
-
栈(stack)
-
描述的是方法执行的内存模型,每个方法被调用都会创建一个栈帧
-
JVM为每个线程创建一个栈,存放该线程执行方法的信息
-
属于线程私有,不能实现线程间共享
-
先进后出,后进先出
-
由系统分配,速度快,是一个连续的内存空间
-
-
堆(heap)
-
用于存储创建好的对象和数组
-
JVM只有一个堆,所有线程共享
-
是一个不连续的内存空间,分配灵活,速度慢
-
在堆上的区域,会被垃圾回收器做进一步的划分,eg:新生代、老年代
-
-
方法区(method area)
-
JAVA虚拟机规范,可有不同实现
-
JDK7以前是"永久代"
-
JDK部分去除“永久代”,静态变量,字符串场池都挪到了堆内存中
-
JDK8是“元数据空间”和堆结合起来
-
-
JVM只有一个方法区,被所有线程共享
-
方法区实际也是堆,只是用于存储类、常量相关信息
-
用来存放程序中永远不变或唯一的内容
-
常量池主要存放常量:文本字符串、final常量值
-
参数传值机制
Java中,方法中所有参数都是"值传递",即我们得到的是"复印件",而不是"原件"。
基本数据类型参数的传值
传递的是值的副本,副本改变不会影响原件
引用类型参数的传值
传递的是值的副本,但引用类型指的是"对象的地址"。因此,副本和原参数指向同一个"地址",改变"副本指向地址对象的值",意味着原参数指向对象地址的值也会改变。
示例
public class Person {int age;String name; public void show(){System.out.println(name);System.out.println(age);} public static void main(String[] args) {Person p1 = new Person();p1.age = 18;p1.name = "张三";p1.show(); Person p2 = new Person();p2.age = 20;p2.name = "李四";p2.show(); Person p3 = p1; // 指向的是p1的对象地址Person p4 = p1; // 指向的是p1的对象地址p4.age = 66;p4.show();System.out.println(p1.age); // 66} }
垃圾回收机制(Garbage Collection)
原理和算法
-
内存管理
-
堆中对象的管理对象空间的分配:使用new关键字创建对象即可
-
对象空间释放:将对象赋值null
-
-
垃圾回收过程
-
发现无用的对象
-
回收无用对象占用的内存空间
-
-
相关算法
-
引用计数法
-
引用可达法(跟搜索算法)
-
通用的分代垃圾回收机制
堆内存模型:
-
年轻代
所有新生成对象首先放在Eden区,存储从未被垃圾回收的新对象,有用的对象放入survivor区,循环存放小于15次,大于15次就放入Tenured区中
Minor GC:清理年轻代区域
-
年老代
年老代存放的都是一些生命周期较长的对象
Major GC:清理老年代区域
-
永久代
Full GC:用于清理年轻代、年老代、永久代,成本高,会对系统性能造成影响
JVM调优和Full GC
以下原因可能导致Full GC:
-
年老代(Tenured)被写满
-
永久代(Perm)被写满
-
System.gc()被显式调用--通知虚拟机调用Full GC
-
上一次GC后Heap的个各域分配策略动态变化
容易造成内存泄漏的操作
内存泄漏:由于某种原因程序未释放,造成内存浪费,导致运行速度慢或系统崩溃。
-
创造大量无用对象
-
静态集合类的使用
-
各种连接对象(IO流对象、数据库连接对象、网络连接对象)未关闭
-
监听器的使用
this关键字
this:当前对象本身(对象的地址)
用法
-
普通方法中,this指向调用该方法的对象
-
构造方法中,this指向正要初始化的对象
-
this()调用重载的构造方法中,避免相同的初始化代码,但只能在构造方法中用,并且必须位于构造方法第一句
-
this不能用于static方法中
-
this作为普通方法的"隐式参数",由系统传入方法中
public class testThis {int a,b,c; testThis(){System.out.println("正要初始化对象:" + this);}// 想调用testThis()的写法: testThis(int a,int b){this(); // 调用的是testThis(),且必须位于第一行// 不能写testThis()a = a; // 这里指的是局部变量,而不是成员变量this.a = a;this.b = b;} testThis(int a,int b,int c){this(a,b); // 调用的是testThis(int a,int b)this.c = c;} void sing(){ } void ha(){System.out.println("当前对象:" + this);this.sing();System.out.println("哈哈哈哈");} public static void main(String[] args) {testThis haha = new testThis(2,3);haha.ha();} }
static关键字
static声明的熟悉或方法:静态变量(类变量)、静态方法(类方法)
特点
-
为该类的公用变量,属于类,被该类的所有实例共享,在类载入时被初始化
-
static变量只有一份
-
一般用"类名.类变量/方法"调用
-
在static方法中不可直接访问非static的成员
public class testStatic {int id;String name; static String school = "成都信息工程大学";public testStatic(int id,String name){this.id = id;this.name = name;} public void login(){System.out.println(name);} public static void printSchool(){// login(); 无法调用非静态成员,会报错System.out.println(school);} public static void main(String[] args) {testStatic a = new testStatic(12,"张三");testStatic.printSchool();testStatic.school = "嘿嘿嘿";testStatic.printSchool();} }
静态初始化块
-
构造方法用于对象的普通初始化
-
静态初始化块,用于类的初始化操作
-
在静态初始化块中不能直接访问非static成员
注意事项
静态初始化块执行顺序:
-
上溯到Object类,先执行Object的静态初始化块,再向下执行子类的静态初始化块,直到类的静态初始化块为止
-
构造方法执行顺序和上面顺序一样
public class TestStatic01 {static String school;static{System.out.println("111111");school = "2222";printSchool();} public static void printSchool(){System.out.println(school);} public static void main(String[] args) {} }
变量的分类和作用域
三种类型:局部/成员/静态
核心区别:
类型 | 声明位置 | 从属于 | 生命周期(作用域) |
---|---|---|---|
局部变量 | 方法或语句块内部 | 方法/语句块 | 从声明处开始,到方法或语句块结束 |
成员变量 | 类内部,方法外部 | 对象 | 对象创建,成员变量跟着创建,消失一样 |
静态变量 | 类内部,static修饰 | 类 | 类被加载,静态变量就有效 |
包机制(package、import)
包(package)相当于文件夹对于文件的作用。用于管理类、解决类的重名问题
两个要点
-
通常是类的第一句非注释性语句
-
包名:域名倒着写即可,再加上模块名,便于内部管理类
命名示例:
com.oracle.test;
com.dubai.zhang.test;
注意:
-
写项目都要加包,不要使用默认包
-
com.zhang 和 com.zhang.san是两个完全独立的包,没有包含关系
JDK中的主要包
表JDK中 | 的主要包 |
---|---|
Java中的常用包 | 说明 |
java.lang | 包含一些Java语言的核心类,如:String、Math、Integer、System、Thread |
java.awt | 包含了构成抽象窗口工具集(abstract window toolkits)的多个类,被用来构建和管理应用程序的图形用户界面(GUI) |
java.net | 包含执行与网络相关的操作的类 |
java.io | 包含能提供多种输入/输出功能的类 |
java.util | 包含一些实用工具类,如定义系统特性、使用与日期日历相关的函数 |
导入类import
如果要使用其他包的类,需使用import,从而在本类中直接通过类名调用。
package com.lisi.test; import com.zhangsan.test.Test01; public class apple {// 不import Test01想要调用的写法: // com.zhangsan.test.Test01 apple = new com.zhangsan.test.Test01();// import 后的写法:Test01 banana = new Test01();}
注意点
-
Java会默认导入java.lang包下所有的类
-
如果导入两个同名的类,只能用包名+类名来显示调用相关类
eg:java.util.Date Date = new java.util.Date();
-
导入一个包下所有的类,加号,eg:import java.util.*
静态导入
static import,JDK1.5新增加的功能,作用:导入指定类的静态属性和方法。
使用
package com.zhangsan.test; import static java.lang.Math.*; // 导入Math类的所有静态属性 import static java.lang.Math.PI; // 导入Math类的PI属性 public class Staticimport {public static void main(String[] args) {System.out.println(PI); // 不用再 Math.PI} }
相关文章:
Java学习之类和对象、内存底层
目录 表格结构和类结构 表格的动作和类的方法 与面向过程的区别 具体实现 对象和类的详解 类的定义 属性(field 成员变量) 方法 示例--编写简单的学生类 简单内存分析(理解面向对象) 构造方法(构造器 constructor) 声明格式: 四…...
递归遍历目录结构和树状展现
在D盘下创建文件夹“电影”,在文件夹“电影”下创建“华语”、“好莱坞”,在文件夹“华语”下创建文件“人民的名义.mp4”、“天安门传奇.mp4”、“程序员统治世界.mp4”,在文件夹“好莱坞”下创建文件“国王的演讲.mp4”、“速度与激情8.mp4…...
【C++的奇迹之旅(二)】C++关键字命名空间使用的三种方式C++输入输出命名空间std的使用惯例
文章目录 📝前言🌠 C关键字(C98)🌉 命名空间🌠命名空间定义🌉命名空间使用 🌠命名空间的使用有三种方式:🌉加命名空间名称及作用域限定符🌠使用using将命名空间中某个成员…...
如何通过针对iOS的动态分析技术绕过反调试机制
在这篇文章中,我们将跟大家介绍和分析一种针对iOS的新型安全研究技术,该技术能够让iOS应用程序的调试过程更加轻松,并解决那些可能会延缓我们步伐的阻碍。 如果你要对一个采用了反调试技术的iOS应用程序或二进制文件进行调试的话,…...
33.Python从入门到精通—Python3 正则表达式 re.match函数 re.search方法 re.match与re.search的区别
33.从入门到精通:Python3 正则表达式 re.match函数 re.search方法 re.match与re.search的区别 Python3 正则表达式re.match函数re.search方法re.match与re.search的区别 Python3 正则表达式 在 Python3 中,可以使用 re 模块来进行正则表达式的匹配和处理…...
便携式气象站是什么
TH-BQX5便携式气象站是一种用于应对突发天气灾害和紧急情况的便携式气象监测设备。它通常包括气温、湿度、气压、风速、风向和降水量等关键气象要素的测量功能,能够快速准确地记录这些气象参数。此外,一些高级的便携式气象站还具备预警功能,当…...
AIGC重塑金融:AI大模型驱动的金融变革与实践
随着人工智能技术的飞速发展,AI大模型在金融领域的应用日益广泛,正在深刻改变着金融行业的面貌。本文将探讨AIGC(人工智能与金融结合)如何重塑金融,以及AI大模型驱动的金融变革与实践。 AIGC重塑金融的背景与意义 随着…...
TP4054替代DP4054锂电池供电电路保护方案
锂离子电池以其优良的特性,被广泛应用于:手机、摄录像机、笔记本电脑、无绳电话、电动工具、遥控或电动玩具、照相机等便携式电子设备中。 01 电池特点 1、具有更高的重量能量比、体积能量比; 2、电压高,单节锂电池电压为3.6V,等…...
前端JS商品规格组合
给定一个数组 let data [{name: "颜色",specs: ["白色", "黑色"],},{name: "尺寸",specs: ["14寸","15寸", "16寸"],},{name: "处理器",specs: ["i5", "i7", "i9&…...
⾃定义类型:联合和枚举
乐观学习,乐观生活,才能不断前进啊!!! 我的主页:optimistic_chen 我的专栏:c语言 点击主页:optimistic_chen和专栏:c语言, 创作不易,大佬们点赞鼓…...
Spring IOC控制反转、DI注入以及配置
1.使用xml的方式进行配置IOC容器,首先引入依赖 在Resource资源下配置,applicationContext.xml ,刷新mevan后可以直接选择配置spring.xml文件 <!-- spring核心用来管理bean --><dependency><groupId>org.springframework</g…...
RabbitMQ的部分模式
1发布订阅模式 发送者 package org.example; import com.alibaba.fastjson.JSON; import com.rabbitmq.client.BuiltinExchangeType; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import ja…...
提取单选框的值,并通过ajax传值到后台
<!DOCTYPE html> <html lang"zh" xmlns:th"http://www.thymeleaf.org" xmlns:shiro"http://www.pollix.at/thymeleaf/shiro"> <head><th:block th:include"include :: header(日库存更新提示)" /> </head&…...
Django创建多app应用
目录 1. 引言 2. 多app创建的两种方式 2.1 多个app结构 2.2 单个apps多个app 3. 最后 1. 引言 在平常业务开发中,我们遇到的功能可能会有很多,单个app的应用可能无法满足我们 这个时候,我们就需要多app应用,例如:…...
如何反反爬虫
我们来讲最常见的反反爬虫方法 import requests r requests.get(网页网址) print(r.requests.headers) 一.使用简单的方法把请求头改为真的浏览器模式 import requests link网页地址 heraders{User-Agent:} rrequests.get(link,headersheaders) print(r.requsts.headers)我们…...
wireshark抓包之DNS协议
DNS协议 DNS协议的主要作用是将域名解析为对应的IP地址。当我们在浏览器中输入一个网址时,计算机需要通过DNS协议来查找该网址对应的IP地址,以便能够建立连接并访问目标资源。 DNS协议的工作流程大致如下: 用户的计算机或设备(充…...
升级到 Java 21 是值得的
升级到 Java 21 是值得的 又到了一年中的这个时候——New Relic 的年度“State of the Java Ecosystem”调查结果出来了,我一如既往地深入研究了它。虽然我认为该报告做得很好并且提出了很好的问题,但我对有多少 Java 开发人员正在使用低版本感到沮丧。…...
C# 多线程
文章目录 C# 多线程进程与线程无参数的子线程带参数的子线程运行结果 销毁线程 Abort()运行结果 ThreadPool和Task运行结果 异步与同步运行结果 lock单线程运行结果 多线程运行结果 使用lock运行结果 C# 多线程 进程与线程 进程:进程就是一个应用程序,…...
快速安装sudachipy日语包
1、前往 https://rustup.rs 下载并安装 Rustup Linux系统可直接运行以下命令 Window系统需要去网站下载exe包 curl --proto https --tlsv1.2 -sSf https://sh.rustup.rs | sh2、安装 Rust 编译器 rustup install stable3、设置默认版本 rustup default stable4、重新安装 …...
蓝桥杯刷题day13——乘飞机【算法赛】
一、问题描述 等待登机的你看着眼前有老有小长长的队伍十分无聊,你突然想要知道,是否存在两个年龄相仿的乘客。每个乘客的年龄用一个 0 到 36500 的整数表示,两个乘客的年龄相差 365 以内就认为是相仿的。 具体来说,你有一个长度…...
大模型量化技术-BitsAndBytes
Transformers 量化技术 BitsAndBytes bitsandbytes是将模型量化为8位和4位的最简单选择。 8位量化将fp16中的异常值与int8中的非异常值相乘,将非异常值转换回fp16,然后将它们相加以返回fp16中的权重。这减少了异常值对模型性能产生的降级效果。4位量化进一步压缩了模型,并且…...
EasyExcel 复杂表头的导出(动态表头和静态表头)
问题:如图,1部分的表头是动态的根据日期变化,2部分是数据库对应的字段,静态不变的; 解决方案:如果不看1的部分,2部分内容可以根据实体类注解的方式导出,那么我们是不是可以先将动态表…...
centos7 fatal error: curl/curl.h: No such file or directory
若编译遇到此问题,可以查看环境是否libcurl库 yum list installed | grep libcurl 发现未安装libcurl库 执行libcurl库的安装命令: 1.对于Debian/Ubuntu系统: sudo apt-get install libcurl4-openssl-dev 2.对于RHEL/CentOS系统…...
【Linux】自定义协议+序列化+反序列化
自定义协议序列化反序列化 1.再谈 "协议"2.Cal TCP服务端2.Cal TCP客户端4.Json 喜欢的点赞,收藏,关注一下把! 1.再谈 “协议” 协议是一种 “约定”。在前面我们说过父亲和儿子约定打电话的例子,不过这是感性的认识&a…...
常见故障排查和优化
一、MySQL单实例故障排查 故障现象 1 ERROR 2002 (HY000): Cant connect to local MySQL server through socket /data/mysql/mysql.sock (2) 问题分析:以上情况一般都是数据库未启动或者数据库端口被防火墙拦截导致。 解决方法:启动数据库或者防火墙…...
选择华为HCIE培训机构有哪些注意事项
选择软件培训机构注意四点事项1、口碑:学员和社会人士对该机构的评价怎样? 口碑对于一个机构是十分重要的,这也是考量一个机构好不好的重要标准,包括社会评价和学员的评价和感言。誉天作为华为首批授权培训中心,一直致…...
python怎么处理txt
导入文件处理模块 import os 检测路径是否存在,存在则返回True,不存在则返回False os.path.exists("demo.txt") 如果你要创建一个文件并要写入内容 #如果demo.txt文件存在则会覆盖,并且demo.txt文件里面的内容被清空,如…...
SAMRTFORMS 转换PDF 发送邮件
最终成果: *&---------------------------------------------------------------------**& Report ZLC_FIND_EXIT*&---------------------------------------------------------------------**&根据T-CODE / 程序名查询出口、BADI增强*&-------…...
探讨在大数据体系中API的通信机制与工作原理
** 引言 关联阅读博客文章:深入解析大数据体系中的ETL工作原理及常见组件 关联阅读博客文章:深入理解HDFS工作原理:大数据存储和容错性机制解析 ** 在当今数字化时代,数据已经成为企业发展和决策的核心。随着数据规模的不断增长…...
算法打卡day23
今日任务: 1)39. 组合总和 2)40.组合总和II 3)131.分割回文串 39. 组合总和 题目链接:39. 组合总和 - 力扣(LeetCode) 给定一个无重复元素的数组 candidates 和一个目标数 target ,…...
开锁做网站怎么样/虚拟主机搭建网站
这花很妖诶,像我不?哦不,我是仙…… 曼珠沙华--彼岸花 曼珠沙华,又称彼岸花。花香传说有魔力,能唤起死者生前的记忆。 春分前后三天叫春彼岸,秋分前后三天叫秋彼岸。。彼岸花开在秋彼岸期间,非常…...
静态页面网站怎么做/电子营销主要做什么
互联网的核心是一系列协议,总称为“互联网协议”。它们对电脑如何连接和组网,做出来详尽的规定。理解了这些协议,就是理解了互联网的原理。 在面试或者工作过程中,我们对我们知道的网络协议侃侃而谈,五层模型…...
php可以独立做网站吗/域名注册平台有哪些
Vue 中的 key 是用来做什么的?为什么不推荐使用 index 作为 key?常常听说这样的问题,本篇文章带你从原理来一探究竟。示例以这样一个列表为例:1 2那么它的 vnode 也就是虚拟 dom 节点大概是这样的。{ tag: ul, children: [ …...
asp.net网站开发视频/网站制作网站推广
吴裕雄--天生自然Django框架开发笔记--Django 模板使用 django.http.HttpResponse() 来输出 "Hello World!"。该方式将数据与视图混合在一起,不符合 Django 的 MVC 思想。将介绍 Django 模板的应用,模板是一个文本,用于…...
wordpress 评论表情插件/aso应用优化
前言 本章内容是android.os.HandlerThread,版本为Android 3.1 r1,翻译来自"雪鹰工作室",再次感谢"雪鹰工作室"!欢迎你一起参与Android的中文翻译,联系我over140gmail.com。 声明 欢迎转载&#x…...
网站制作书籍推荐/百度关键词优化软件排名
bzoj3944:Sum 裸的杜教筛 bzoj4916:神犇和蒟蒻 按照筛phi的方式稍微推一下就好 bzoj3512:DZY Loves Math IV 我打了一个神奇的(杜教筛记忆化搜索(搜索里面有两层根号的找约数,总体上是在利用mu来容斥)),这种方法虽然不优秀但是也能过,然而正解是(杜教筛记忆化搜索(充分利用phi函…...