为什么要引入面向对象的思想?
我们可以将具有某一功能的代码封装到方法中,这为我们调用这个代码提供了方便,在我们需要用到这个功能时只需要调用相应的方法即可,提高了代码的复用性。但是因为我们程序中常常有多个方法的加入,所以我们想到将方法也封装,即 用 类 封装多个方法。在以后的调用中,我们只需要先找个这个 类 ,然后使用这个类中的方法 ,这种引入就是面向对象的编程方式。面向对象思想的特征:
1)面向对象更符合我们的生活习惯
2)面向对象使我们从执行者变成了指挥着,指挥对象做事情
3)面向对象使复杂事情简单化
面向对象的三大特征:
1)封装:
把对象的属性和操作(或服务)结合为一个独立的整体,并尽可能隐藏对象的内部实现细节。
2)继承:
从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。
3)多态:
多态就是同一个接口,使用不同的实例而执行不同操作
面向对象开发:
就是不断的创建对象,使用对象,指挥对象做事情。
面向对象设计:
管理和维护对象之间的关系。
创建对象 ---- 使用对象 ----指挥对象做事情 实例:
public class Phone {
//成员变量
String brand ;
int price ;
String color ;
//成员方法
public void print() {
System.out.println("这是我的手机:");
}
}
/** * Phone类的测试类
* 全路径: 包名.类名@地址值标记
*
* @author admin
*
*/
public class PhoneTest {public static void main(String[] args) {
//对Phone进行调用
//创建一个Phone对象
ph Phone ph = new Phone();
//引用类型 ph是一个地址
//使用对象//给ph中的元素进行赋值
ph.brand = "HUAWEI";ph.price = 2000 ;ph.color = "黑色" ;
//指挥对象做事情
ph.print();
System.out.println("品牌是:" + ph.brand + " 价格是 :" + ph.price + " 颜色是 :" + ph.color);}}
对象的内存分配
java中的内存
java中为了提高程序的执行效率将内存划分为5个区:
1)栈内存:可以存放局部变量
2)堆内存:存放new出来的东西
3)方法区:
①又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
②方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
③运行时常量池都分配在 Java 虚拟机的方法区之中
内存有栈内存、堆内存。程序在执行时,首先进入main方法,由main方法在栈内存中开辟一个区域,加载类Phone。当执行Phone ph = new Phone();时,在堆内存中创建一个Phone类区,,区中存放成员变量、成员方法等。成员变量未被,初始化前会有系统默认初始化,对于字符(串)型初始值为null ,int型为0 。进行方法标记。
调用成员方法的话,一旦调用结束后立刻shut down(放入垃圾回收器中,释放内存)。
创建两个对象的内存分配:main函数首先在栈内存中开辟一个空间,加载类加载方法Phone p申请一个栈内存空间 , 给new Phone()分配一个堆内存空间。第二个对象 p2 在创建时同样在栈内存中申请一个空间(假设内存地址为0x002),new Phone同样会在堆内存中得到空间分配地址为0x002。系统会默认初始化,多个变量(如图中brand,price,color)指向堆内存。调用class类中的方法时,方法一旦执行完毕即挂掉。
创建三个对象,有多个对象指向同一个堆内存时(如下图中的对象p和 p3,因为将p传递给了p3),它们会使用同一个Phone类区,因为p3这个对象不用开辟一个新的堆内存区,所以这种创建方法有省内存的效果。
成员变量和局部变量的区别
什么是成员变量?
成员变量是在类体的变量部分中定义的变量,也称为属性。
成员变量的定义是有要求的:如果该类中本身有这个属性才可以定义为成员变量。 例如我们把一个长方形看成一个类,那么这个类的长方形的长、宽可以看作是这个类中的成员变量,长方形的面积和周长可以分别看成是这个类的成员方法。
位置: 成员变量在方法外类中,局部变量在方法声明上或方法定义中。
在内存中的位置: 成员变量在堆内存中方,局部变量在栈内存中。
生命周期: 成员变量随类的加载而加载,消失而消失,局部变量随方法调用而存在,调用完毕而消失。
初始化值不同: 成员变量初始化有系统默认初始化,局部变量由我们初始化,使用前初始化使用。
使用对象名访问类中的成员变量和成员方法
匿名对象
匿名对象顾名思义就是没有名字的对象,这在我们以后的接口中会常常使用到,现在我们只需要知道匿名对象的格式就可以了。直接创建的对象是我们Phone这个类的地址,我们还可以直接创建这个匿名对象的变量并操作。
//假设有一个Phone类,我们可以直接定义一个Phone的匿名对象。
new Phone() ;
new Phone().brand = "HUAWEI" ;
new Phone().price = 2000 ;
我们再使用匿名变量时,每定义一个匿名变量就会开辟一个新的内存空间,这样堆内存会造成浪费,如果创建太多甚至会造成内存溢出,所以Java建议每个类
只创建一个创建匿名对象。
引用类型作为形式参数
一个类①的功能可能在另一个类②的方法中被使用,这样,我们需要在调用者的方法中创建被调用者的对象,这是一种传递;还有一种是,当调用者②的形式参数为被调用者①的对象(即引用类型作为形式参数)时,我们在其他方法③中调用②这个类中以①为形式参数的方法时,需要在③中同时创建②和①的对象。例如:
class PhonePrint{
public void PhonePri() {
System.out.println("这是一个作为形式参数的类。。");
}
}
class PhoneCommd{
public void PhoneCom(PhonePrint p) { // 这里定义一个引用类PhonePrint的对象p作为形式参数
//调用p对象的PhonePrint方法
p.PhonePri();
System.out.println("我调用了PhonePrint作为我的形式参数");
}
//我再创建一个不用PhonePrint类的对象作为形式参数的方法
public void Try() {
System.out.println("我不用PhonePrint这个类的对象");
}
}
public class PhoneTest {
public static void main(String[] args) {
//创建一个PhoneCommd类的对象pc
PhoneCommd pc = new PhoneCommd();
//这行就错了 pc.PhoneCom(p); // 因为形式参数是一个引用类型,所以无法直接调用PhoneCom
pc.Try();
//创建创建一个PhonePrint的对象
PhonePrint pp = new PhonePrint();
pc.PhoneCom(pp);
}
}
形式参数的改变会直接影响实际参数。
private关键字
为什么要引入private关键字?
举一个例子:我们现在创建一个学生类,这个学生类中有学生的局部变量和局部方法存放信息,如年龄等。但是我们没有给这些局部变量定一个范围,例如当输入 -30时就会输出-30,这显然不是一个合理的年龄。然后我们尝试在我们的学生类中创建一个判断方法以限定年龄的范围,并在调用者那里执行这个判断。照此执行之后,我们发现虽然我们输出了判断后的结果,但是我们也输出了没有判断的结果,即输出了 30 和 -30 。也就是说成员变量可以永远的被调用者访问,针对这种情况,我们引入了与public关键字相对应的 private关键字,将成员变量私有化,外部类无法直接访问。
外部类如何访问private定义的变量?
虽然private修饰的成员变量无法被外部类直接访问,但是我们可以用一个带有public修饰的方法来间接访问。我们需要对私有的成员变量进行输入和输出,故我们引入SetGet方法。例如:
class Var{
//创建私有化的成员变量
private String var;
private int vari;
//创建公共访问方法
public String getVar() {
return var;
}
public void setVar(String var) {
this.var = var;
}
public int getVari() {
return vari;
}
public void setVari(int vari) {
this.vari = vari;
}
}
public class test {
public static void main(String[] args) {
//无法直接访问私有变量var 和 vari
//所以我们创建了他们的公共访问方法 快捷键alt +shift + s + v
Var v = new Var();
v.setVar("就是想试一试");
v.setVari(2333);
System.out.println(v.getVar() + " 这是我们输出var的值 ," + v.getVari() + " 这是我们输出vari的值。");
}
}
Set方法是我们给私有成员变量设值的方法,Get方法是我们得到私有成员变量值的方法。这两个方法给我访问私有变量搭建了桥梁。
在Set Get方法中,为了防止同名的局部变量对成员变量的隐藏,我们使用了
this关键字,this关键字代表的是当前类的对象,是一个地址值。所以我们用this.var 就指定了这个var为成员变量而不是形式参数。this关键字在内存中的运作方式如下图:
构造方法
构造方法就是给对象进行初始化。构造方法也属于类的组成,和成员变量,成员方法一样。构造方法也是可以重载的。
构造方法的特点:
(1)方法名和类名相同 (2)构造方法没有返回值,连void都没有
构造方法的分类:
(1)无参构造:给对象进行初始化。当我们没有提供无参构造时,系统会默认给我们提供一个无参构造,所以上面的代码没有问题。。。那么如果我们写了无参构造或者有参构造,则系统不会给出。
class Test1 {
public Test1() { // 方法名和类名相同;没有返回值,连void都没有
System.out.println("这是一个无参构造方法");
}
}
(2)有参构造:顾名思义就是带有参数的构造方法。我们建议永远给出无参构造,即使存在有参构造。我们可以给一个参数,也可以给多个参数。我写了一个有参构造的例子:
class Demo{
//成员变量
private String brand ;
private int price ;
private String color ;
public Demo(){//这是一个无参构造进行初始化
}
public Demo(String brand , int price , String color) {//这是一个有参构造
this.brand = brand ;
this.price = price ;
this.color = color ;
}
public String getBrand() {
return brand;
}
public int getPrice() {
return price;
}
public String getColor() {
return color;
}
}
public class DemoTest {
public static void main(String[] args) {
Demo d = new Demo("HUAWEI", 2000, "黑色");//创建一个有参构造的对象
System.out.println("品牌:" + d.getBrand() + " 价格:" + d.getPrice() + " 颜色:" + d.getColor());
}
}
构造方法在内存中的图解:成员方法一个标准的类的格式
上面我们已经解释了一个类的组成包括:成员变量、成员方法、构造方法。那么我们就可以用这些来做一个标准的类。
格式如下:
class 类名{
定义成员变量(private修饰);
定义构造方法(无参构造(+有参构造));
定义成员方法;
}
实例如下:
class standardclass {//class 类名
//定义成员变量
private int member_variable1 ;
private char member_variable2;
//......
private String member_variablen;
//构造方法:无参构造+ (有参构造)
public standardclass() {//无参构造
super();
}
public standardclass(int member_variable1, char member_variable2, String member_variablen) {//有参构造
super();
this.member_variable1 = member_variable1;
this.member_variable2 = member_variable2;
this.member_variablen = member_variablen;
}
//成员方法:SetGet方法 + 我们需要的方法
public int getMember_variable1() {
return member_variable1;
}
public void setMember_variable1(int member_variable1) {
this.member_variable1 = member_variable1;
}
public char getMember_variable2() {
return member_variable2;
}
public void setMember_variable2(char member_variable2) {
this.member_variable2 = member_variable2;
}
public String getMember_variablen() {
return member_variablen;
}
public void setMember_variablen(String member_variablen) {
this.member_variablen = member_variablen;
}
//比如说我建立一个方法打印“这是一个标准类”
public void printStand(){
System.out.println("这是一个标准类");
}
}
static关键字
为什么要引入static关键字?
比如我们现在要输出一些网络游戏的名字、发行日期、出品公司等,我们需要创建一个网络游戏类,在这个类中,我们有成员变量名字、发行日期、出品公司等等。但是当我们统计英雄联盟、QQ飞车、创越火线时,我们会发现,有多个对象对同一个变量进行共享。如果我们给每一个对象的出品公司都赋值腾讯话会造成堆内存的无意义浪费,所以我们引入静态(共享)关键字static以节省堆内存。
class Game{
//成员变量
String name ;
int year;
static String manufacturer ;//因为制造商都是腾讯,我们用static把他共享
//构造方法
public Game() {
super();
}
public Game(String name, int year,String manufacturer) {
super();
this.name = name;
this.year = year;
this.manufacturer = manufacturer ;
}
public Game(String name, int year) {
super();
this.name = name;
this.year = year;
}
public void show() {
System.out.println("产品名:" + name + " \t出品时间:" + year + " \t出品商:" + manufacturer);
}
}
public class GameTest {
public static void main(String[] args) {
Game g1 = new Game("cf", 2008 , "tencent");
g1.show();
Game g2 = new Game("lol", 2011 );
g2.show();
Game g3 = new Game("QQspeed", 2005);
g3.show();
System.out.println("------------------------");
//
g3.manufacturer = "腾讯" ;
g1.show();
g2.show();
g3.show();
}
}
这是代码的运行结果:
可以看到当我们将静态变量分配给一个构造方法对象时,其他的对象会自动获得这个共享的属性。当我们修改任意一个对象的manufacturer时,我们发现,我们修改的其实是一个静态manufacturer的值,我们将这个值分享给了其他Game类的对象。这就是静态定义的作用效果。
关于static关键字:
(1)静态的加载是随着类的加载而加载的(加载类时 首先被加载)。
(2)static优先于对象而存在。
(3)static可以共享数据,可以被多个对象共享。
(4)如果数据被static修饰,那么该数据可以直接被类名调用;
①被静态修饰的变量可以被类直接调用 格式: 类名.变量名
②被静态修饰的方法可以被类名直接调用 格式: 类名.方法名()
开辟静态工作区在内存中的过程如下:
通过这个过程我们可以看到:我们在堆内存中创建第一个对象时将“西施” 27这两个值赋给了给成员变量,而“中国”这个被static修饰的值则被送到了static区的静态标记中了,内有占用堆内存。在我们创建第二个、第三个对象的时候,对于country这个公共变量我们只需寻址在static中的静态标记查询就可以了,这就是他省堆内存的原因。
static用法:(静态只能访问静态)
①一个类中即可以有静态成员变量,也可以有非静态成员变量
②可以有静态成员方法也可以有非静态成员方法
③静态方法只能访问静态变量或者静态方法
④非静态方法既可以访问静态变量也可以访问非静态变量