目录
温馨提示:从类开始看,主要看图、图、图
前言
前文讲述的是静态变量和常量,现在讲它的另一半动态行为:方法
方法(在c中也可称为函数)
方法创建(形参),如创建的方法无返回值类型,则为void
返回值类型 方法名(形式参数列表/*形式参数前面写上参数类型,第3次提醒*/) {
Java语句;
}
方法调用(实参)
对象名.方法名(实参列表)
扩展部分:
方法重载(个人不喜欢)
本质:形式参数列表不同(为了方法能取相同的名字,也是拼了)
static void add(){}
static void add(int a){}
static void add(int a , double b){}
static void add(double b, int a){}
递归:方法自己调用自己
如:深度优先搜索(Depth First Search, DFS)
public static void main(String[] args) {
int result = factorial(5);
System.out.println("结果为"+result);
}
public static int factorial(int n){
System.out.println("等于");
if(n==1){
return 1;
}else{
return n*factorial(n-1);
}
类
Java是一种典型的面向对象语言(区别于C,面向过程)
面向对象:类
和表格联系起来:列(也叫字段,field)
表格 | 对象 |
学生结构: //属性(静态数据) ID 姓名 学号 //方法(动态行为) 一日安排: 1.学7个小时 2.打羽毛球 | class Student { int id; String name; int number;
void YiriAnpai(){ System.out.println("学7个小时”); System.out.println("打羽毛球"); } } |
/**
* Student类
* 内存分析辅助我们更好理解面向对象
*/
public class Student {
int id;
int age;
String sname;
public void study(){
System.out.println("学7个小时");
}
public void badminton(){
System.out.println("打羽毛球");
}
/*如果没定义构造器,编译器会自动定义一个无参的构造方法。构造器的方法名必须和类名一致。本例为:
SxtStu(){}
*/
public static void main(String[] args) {
Student s1 = new Student();//这个s1不是对象哦,它是一个引用
System.out.println("学号是"+s1.id);
System.out.println("名字是"+s1.sname);
System.out.println("***********可见上面是初始化值,赋值(值传递后),打印***********");
s1.id = 1202121734;
s1.sname = "希";
System.out.println("学号是"+s1.id);
System.out.println("名字是"+s1.sname);
s1.study();
s1.badminton();
}
}
图片说明(能看图的尽量看图,看不懂再看文末的啰嗦文字)
线程;thread;栈stack;堆heap;方法区method Area(也是堆,JDK8是”元数据空间“和堆的结合);
线程1 | 栈1:线程私有,不能实现线程间的共享 | ||
线程2 | 栈2:先进后出 | 堆:垃圾回收机制分成新生代和老年代,System.gc()通知清理垃圾堆 | 方法区 |
存放线程执行方法的信息:实际参数、局部变量; | 存储创建好的对象(和数组,也是对象)成员变量 | 存储程序中永远不变或唯一的内容:类(静态变量)、常量 | |
连续内存空间,系统自动分配;速度快;方法执行 | 不连续内存空间,分配灵活;速度慢;对象管理(创建) | 加载1次 |
面向对象(三大特性)
说明:此处为IDEA开发集成环境界面,先创建两个包,分别名为Bao1和Bao2。其中Bao2创建了我们上文的Student()类,讲述了源文件是如何根据需求(类有哪些静态变量、方法)进行编写的。
以下来介绍Bao1,先在src(source, 源文件)创建一个Animal.java类和TestPolym.java类。
三种特性的实例
先在第一个源文件Animal.java中定义好动物(Animal)的叫(shout方法),再弄两个子类狗和猫,对这个叫进行升级改造(方法重构override)
public class Animal {
public/*封装*/ void shout(){
System.out.println("叫");
}
}
class Dog extends Animal/*继承*/{
@Override
public void shout(){
System.out.println("汪汪汪!");
}
public void seeDoor(){
System.out.println("看门");
}
}
class Cat extends Animal/*继承*/{
@Override
public void shout() {
super.shout();
}
}
在第二个源文件TestPolym.java(TestPolym类)中实现多态的方法:子类不同,即发出不同的叫
public class TestPolym {
static void animalCry(Animal a)/*父类-实例对象*/{
System.out.println("TestPoly.animalCry");
a.shout();
}
/*以上的多态方法,等价于
static void animalCry(Dog a){
System.out.println("TestPoly.animalCry");
a.shout();
}
static void animalCry(Cat a){
System.out.println("TestPoly.animalCry");
a.shout();
}
*/
public static void main(String[] args){
System.out.println("*******多态部分*****");
Dog d1 = new Dog();
animalCry(d1);//子类-实例对象
animalCry(new Cat());//或直接在里面建也可
System.out.println("*******转型部分*****");
Animal a1 = new Dog();//向上转型(自动)
a1.shout();
//以下是子类新增方法(父类没有)的处理
//编译类型 运行时类型:先通过编译器编译(编译只认Animal),再运行(运行没问题)
Animal a2 =new Dog();//向上转型(自动)
/*a2.seeDoor();//出错,解决方案1:Animal父类中添加seeDoor方法*/
if(a2 instanceof Dog) {//如果实例对象a2的种类是狗(当然它前提肯定是动物)
Dog d2 = (Dog) a2;//解决方案2:说明这只动物(父类)是狗(哪个具体子类),称作向下转型(强制)
d2.seeDoor();
}
}
}
出现问题:
注意能够运行的不一定能够编译。针对编译器将实例对象视为仅为Animal,解决方案有两种:
1、在Animal类中创建seeDoor()方法
2、向下强制转型,判别了这只动物(Animal)a2也是狗(Dog)的种类
((Dog) a2).seeDoor();
或文中
Dog d2 = (Dog) a2;
d2.seeDoor();
继承、封装、多态
(对象)继承
类只能单继承(头顶只能一根线),使用:extends
(访问)封装
访问修饰符private(类内)、default(包内)、protected(+父类)、public(全部)
private属性私有后,[访问修饰符为public]的get/set方法访问
protected不在同一包中,虽可访问父类对象的protected成员,但不能访问父类实例对象的protected成员(堆)
(方法)多态
根据子类的不同,可以实现不同的方法。
附注:啰嗦文字(能看图的尽量看图,看不懂再看啰嗦文字)
线程
一个线程对应一个栈--多个栈对应一个堆和一个方法区
栈
存放执行的方法
参数传值机制s1
值传递,传递的是值的副本(s1)而不是真实对象,但这个副本的改变会让真实对象改变(如s1.id=1202121734,这个只是改变副本s1,但达到了改变真实对象的目的。)
this指向对象
普通方法中指向调用该方法的对象、构造方法中指向初始化的对象
默认值
创建对象后,未初始化的默认值
int | 0 |
char | null |
堆
存放新创建的对象(普通方法属于对象)
不需要java程序员考虑内存管理,只需要考虑对象管理:
对象空间的分配:使用new关键字
对象空间的释放:将对象赋值null(没人吃饭就该清洁阿姨来进行垃圾回收了)
Java提高了开发效率(java专注业务逻辑,区别于C++程序员需要考虑内存管理)
扩展:
垃圾回收机制(Garbage Collection)
引用计数法:堆中每个对象都引用一个引用计数器。如有引用指向对象1时,+1;指向对象2的引用失效(引用变为null,顾客吃完饭了)。缺点:循环引用
public class Student {
String name;
Student friend;
public static void main(String[] args){
Student s1 = new Student();
Student s2 = new Student();
//堆栈中的两个对象开始循环引用
s1.friend = s2;
s2.frient = s1;
//至此,我们也可以知道s1和s2是分别指向两个对象的引用
s1 = null;
s2 = null;
}
}
引用可达法(复杂一些)
方法区
存放类(静态方法属于类),常量
方法重写(Override)
子类在继承父类方法的基础上加以改进(方法重载Overload只是想方设法改相同名字,毫无关系)
运行时常量池
总共有运行时常量池,全局字符串常量池和文件常量池。
抽象类
抽象类包含抽象方法(abstract方法定义的一种规范)的类,没有方法体,只能声明(引用变量类型。
使用方法:需要子类定义具体实现;注:不能创建实例(指不能用new 创建)
public abstract class 抽象类名{
方法定义;//抽象,如public abstract void 方法名()
}
接口是更抽象的抽象类,访问修饰符只能是public或默认
之前要求全部是抽象方法,但Java8及之后允许在接口定义默认方法和静态方法;接口可以多继承
[访问修饰符public或default] interface 接口名 [extends 父接口1, 父接口2 ,...]{
常量定义;
方法定义;//以前抽象,现在新增静态
}
内部类
作用:更好的封装;规则:只能让外部类直接访问,而不是同一个包(有点像private)
内部类只是一个编译时概念,一旦编译成功,成为完全不同的两个类
分类:成员内部类(非静态内部类、静态内部类),匿名内部类,局部内部类
方法内部:局部内部类(少)
特殊类String
不可变字符序列,位于java.lang包中。(java将String视作一个类,注意其他语言可能将其作为一种数据类型)
Unicode字符序列,如Java是由4个Unicode字符J、a、v、a组成
实例:用双引号括起来的”...",就是种类为String的对象