目录
Java类和C++类的定义
Java:类{ }后无分号
普通外部类存在修饰符的选择:default/public(访问权限)和final
C++:类{ }后有分号
只存在函数或者变量访问权限的选择
数据类型方面
Java分为基本数据类型和引用数据类型
基本数据类型的变量,存储的是值本身;
引用数据类型的变量,存储的是对象的引用值,在Java里也称对象的地址。所以引用数据类型的变量好像可以理解为C++里的指针(理解成指针,好像就都能理解了),那么对引用数据类型变量的操作都是对地址的操作(这样好像也可以解释为什么Java没有指针这一说法)
String s1=new String("hello");
String s2 = s1;
//此时s2和s1这两个变量(相当于C++指针)指向栈中的同一内存地址
//这个内存地址指向堆内存里的值"hello"
s1 = "hi";
//此时s1被赋值一个新的地址,这个地址指向的值为"hi"
//s2此时还是原地址
System.out.println(s1 == s2);//false
System.out.println(s1);//hi
System.out.println(s2);//hello
Student m = new Student(19);
Student n = m;
//此时m和n这两个变量(相当于C++指针)指向栈中的同一内存地址
m.score = 20;
//修改这个地址指向的值
System.out.println(m == n);//true
System.out.println(n.score);//20
//两个当然会同时变啦
这两个的区别是:
String的例子中,s1被一个新的String赋值,所以s1中存储了这个新的String的地址,但原地址(s2)指向的对象的值并没有被改变;
Student的例子中,通过m存储的地址直接改变了被指向的对象的值,但并没有改变m和n都指向这个对象这件事。
基本数据名的增加
Java中增添了为单字节整数型byte的基本数据类型
有符号,数的范围是-128-127
类型转换
低字节都可以自动向高字节转换
但是强转的写法有差异
//Java只能这么写:
byte i = 1;
int a = (int) i;
数据名的差异
C++:bool
Java:boolean
而且boolean与其他基本数据类型之间不可互相转换,也不能用0或1对其赋值
赋值的差异
默认浮点型是双精度类型,若想定义单精度类型float必须在浮点数后面加上F或f
float a=1.23; //ERROR!
float a=1.23f; //√
不同数据类型的初始化
声明一个变量后,Java会给出默认值:
boolean:false
char:'\0'
int/byte/short/long:0
float/double:0.0
引用数据类型:null <引用数据类型此时还未引用任何对象,所以其实不算初始化吧>
Java没有指针
使用new开辟一个空间时
单个对象
//C++的写法:
string *s1 = new string("hello");
//*s1相当于一个string
string *s2 = new string;
string *s3 = new string();
//*s2与*s3都相当于执行了默认的无参构造
//JAVA的写法
String s = new String("hello");
//s相当于一个String(从输出来看,其实s中存储的还是地址)
String s1 = new String;
//s1相当于只是存储了一个地址值,但并没有引用对象,相当于是C++中的空指针
//ERROR!!!!!报错
String s2 = new String();
//s2指向一个对象,这个对象已经被String的默认无参构造构造出来
“new运算符为对象动态分配内存空间,并返回一个它的引用值。”
“引用值就是系统分配给对象的内存地址,由于String是一种引用数据类型”
“因此变量中存储的值不是对象本身,而是引用值(栈内存的地址,指向堆内存的对象)”
数组
//C++ new出一个数组
string* s = new string[10];
//这里的*也不可以替换成[ ]!!!!
//每个s[i]都调用了string的默认无参构造,并不是null,而是一个空值
Java虽然没有指针,但有[ ]:
英姿说 * 与[ ]等同应该是针对C++来说的,在Java里面并不适用
//Java new出一个数组
String[] s = new String[10];
//但还没有定义具体的对象
//就相当于 String s[i]
//此时如果输出s[i],看到的将是null
//书上是这么说的:
//“创建一个数组对象s,s的内部包含10个元素,每个元素的类型都相同,但s的值为null,还没有引用任何对象”
//所以还要写接下来这一步
for(int i=0;i<s.length;++i){
s[i]=new String();
//此时输出s[i]就只是空了
//“为数组中的每个元素进行赋值”
}
//这个length好像是只要是个数组就自带的东西,也不是函数(因为没有())
//好神奇啊
关于初始化数组那些事儿
//C++初始化数组
int *i = new int[10]{1};//若把{}去掉,则每个数都为不确定的值
cout << i[0];//1
cout << i[1];//0
A<int> *r = new A<int>[1]{A<int>(0, 1)};//在new后面的{}中调用模板类的构造函数
r[0].display();
B b[] = {B(0, 1)};//直接调用B的构造函数
b[0].display();
//java初始化数组
//java中不存在new后面的{}中进行初始化的写法!
int []a={0,1,2,3};//“这种(不用new的)初始化方法仅限于8中基本数据类型和String型,若为引用类型,则需要采用new关键词对每个对象进行实例化” 而且这里【】中如果写4居然会报错!!说是不允许给数组初始值设定项
int[] b = new int[4];//b[i]默认为0
System.out.println(b[0]);//0
notNamed[] n = new notNamed[1];//n[i]默认为null,即未引用任何对象
n[0] = new notNamed(1);//引用对象时还必须要用new
n[0].display();//1
类内this的使用
//C++的写法:
this->function();
//JAVA的写法
this.data;
this.function();
//this表示程序运行到此处时代指的对象
Java中使用this执行构造函数:
this(<可有参数>);
//如果有的话,必须是子类构造方法中的第一条语句
变量的作用域
Java中的main方法位于具体的类里面,故该类内的成员变量为全局变量,作用范围是整个类。main方法可以直接通过.来访问某个对象的非静态变量并进行修改(当然前提是要有对象,所以需要先new出来)。
对于有static修饰的变量,为静态成员变量,不属于任何一个对象,而属于这个类,故成为类变量;同理,被static修饰的方法成为类方法。它们被调用的时候不需要实例化,直接通过类名(对象也可)+ . +类变量(类方法)调用(C++中,若使用类名,需要将 . 替换成作用域符号:: 。这个符号好像都被Java中的 . 取代了)
final和const
final修饰变量
Java中的final和const相似,被final修饰的基本数据类型不可再被更改,引用数据类型指向的内存不可被更改(呼应了之前提到的指针),即不能指向别的对象。
相当于C++中
int* const p
final修饰的成员变量的初始化没有C++中const这么苛刻,想在哪里初始化都可以,但是都是初始化之后就不能更改了
final修饰类
不可被继承
final修饰方法
不可被子类覆盖
类的访问权限
C++和Java的类中,private都是只能限制在本类中访问;
但是Java中扩充了包的概念,使得存在第四种访问权限:默认default即不额外声明时,可被同一包中的所有类访问;
protected:不但可以被包内访问,还可以被包外的子类访问;
public:可以被所有类访问
继承
1.JAVA只支持单继承,不支持多继承,但允许有多个祖类,即只是不能出现菱形继承
2.JAVA继承关键字:extends
public class subclass extends superclass{
//子类的构造方法中若没有使用super关键字,则系统会默认调用函数super()
//即父类的无参构造,若父类不存在无参构造,则会报错
}
//subclass:子类名
//superclass:超类(父类)名
C++:
class subclass:public superclass{
};
3.访问权限
由于private的访问范围被限制在本类中,故不可以被子类访问(继承),当然也就不能被覆盖
覆盖
子类和父类中的成员变量或方法同名时,正常声明定义子类对象并使用时父类中的成员变量和方法会被覆盖(比C++要求更高:要求方法名 参数列表 返回类型 修饰符号(static)全部都要相同。如果不相同那就直接继承,即子类也可以继续调用未被覆盖的同名函数)
如果是方法重载,那么一定存在形参的个数、类型或者顺序中的某个或某些不同,否则就是方法覆盖,这就要求方法名 参数列表 返回类型 修饰符号(static)全部都要相同(访问权限可不同),不然会编译错误
所以不能在父类和子类中出现同名且参数相同的但static不一致 < 修饰符号不同 > 的方法,这样的话多态的对象就不知道到底要调用哪个父类还是子类的方法了
若想要调用父类的成员变量或者方法,可以使用super关键字
super.xxx
与this相似,可以在子类方法中使用super构造父类对象 :
super(<可有参数>);
//如果有super的话,必须是子类构造方法中的第一条语句
- this 关键字特指本类的对象引用,使用 this 访问成员则首先在本类中查找,如果没有,则到父类逐层向上找;
- super 特指访问父类的成员,使用 super 首先到直接父类查找匹配成员,如果未找到,再逐层向上到祖先类查找
访问权限
继承的方法一定一定要注意访问权限的问题!!!
1.抽象类的pubic方法,继承的子类该方法也要是public!
2.包文件里面的类,要在包外进行测试,那么包内被访问的方法一定要是public!!
3.若要通过抽象类型的对象引用子类调用已经被覆盖抽象方法,那么抽象类的抽象方法也要是public!!!
多态
如果用子类对象给父类对象赋值,那么变量是父类的,方法是子类的
“动态方法调度机制仅对方法有效,对成员变量无效。因此,如果子类存在和父类同名的变量,那么向上转型生成的对象调用该变量时使用的仍旧是父类的变量”
“简而言之,是被引用对象的类型(子类),而不是引用变量的类型(父类)决定执行哪个版本的方法”
super m = new sub();//new出一个子类对象赋值给父类对象
//或者是子类给父类直接赋值
//或者是子类先强转成父类再重新赋值
System.out.println(m.score);//输出父类的成员变量
m.f();//调用子类的方法
//以上都建立在子类和父类都包含变量score的基础上
//若父类和子类都具有相同的变量名,即都被显式地声明出来,
//那么这两个变量完全独立,没有半毛钱关系
//这样的话子类构造方法修改的成员变量就是自己的成员变量了
//若只有父类有score,而继承了父类的成员变量score子类的构造方法中更改了score的值
//父类的成员变量score也将是被修改后的值
//可能是因为子类构造时直接修改了值
这里的引用变量类型即父类相当于C++中指向基类的指针,能多态的方法是能被覆盖的方法(函数名、参数列表、返回类型、修饰符号(static)完全一致 这与C++中覆盖虚函数的要求相同)当然,如果是子类中出现的新方法,包括由于方法重载出现的,父类的对象就无法调用
最后别忘了父类中final修饰的方法不能被子类覆盖
与C++相似,由向上转型而来的父类对象,也没法调用子类中新出现的方法
练习:
public class Main {
int score;
Main() {
score = 10;
System.out.println("父类构造函数" + score);
}
public static void main(String[] args) {
Main m = new sub();
//构造一个sub对象<先调用父类构造函数,再调用子类构造函数>,然后向上转型,因为没有new出一个Main对象,所以不单独进行Main类的构造
System.out.println(m.score);//父类的变量
m.f();//子类的方法
//动态方法调度机制仅对方法有效
System.out.println("---------------------------");
((sub) m).f();//本来就是子类,所以调用子类的方法
System.out.println(((sub) m).score);//本来就是子类,所以调用子类的变量
((Main) ((sub) m)).f();//多态:子类的方法
System.out.println(((Main) ((sub) m)).score);
//输出父类的成员变量:动态方法调度机制仅对方法有效
System.out.println("---------------------------");
//如果想输出子类的成员变量,可以通过调用子类的方法
System.out.println(m.getScore());//调用子类的方法,输出子类的变量:20
m.setScore(5);//调用子类的方法,修改子类的变量
//如果子类未覆盖该方法,那么调用父类的方法,修改的就是父类的变量,而子类的成员变量不变
System.out.println(m.getScore());//调用子类的方法,输出子类的变量:5
System.out.println(m.score);//输出父类的变量:10
System.out.println("---------------------------");
//试试子类与父类的类型强转究竟是怎么个事:
Main n = new Main();
n.f();//父类的方法
n = m; //n与m指向同一个向上转型的子类对象
//所以n也开始多态:
n.f();//子类的方法
System.out.println(n.score);//父类的变量
n = (sub) n; //因为n本身关联向上转型的子类,所以这里可以无痛转子类
n.f();//子类的方法
System.out.println(n.score);//子类的成员变量!
n = (Main) n;//又转回父类
n.f();//还是子类的方法!!
System.out.println(n.score);//父类的成员变量!
n = new Main();
n.f();
n = (sub) n;//ERROR!!!因为未关联过任何子类的对象
}
void f() {//测试父类还是子类的方法
System.out.println("父类");
}
public int getScore() {//测试父类和子类的同名成员变量是否独立
return score;
}
public void setScore(int score) {
this.score = score;
}
}
class sub extends Main {
int score;
sub() {
score = 20;
System.out.println("子类构造函数" + score);
}
void f() {
System.out.println("子类");
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
总结:
对于成员方法:多态
即运行时才知道要调用哪个方法
子类向上转型时,同一块空间,只是被解释的类型不一样:
解释的方式取决于变量的类型,父类用父类的方式去解释, 只是有些父类中的原方法已经被子类覆盖;
对于成员变量:
取决于变量的类型本身。
如:
father = son; //子类向上转型 System.out.println(father.x);//father的类型还是父类,输出父类的成员变量 System.out.println(((Son) father).x);//类型强转之后,father的类型变成子类的成员变量,输出子类的成员变量
抽象类
JAVA抽象类和抽象方法:abstract定义抽象类和抽象方法
abstract class shape{//似乎就是不带像public的修饰符的
int shapeId;
abstract protected double area();
...//其他普通方法
}
C++抽象类:virtual定义纯虚函数
class Pet{
public:
virtual void speak ()=0;
//声明纯虚函数:初始化其为0
};
//告诉用户当前这个 speak 函数没有实际意义的;
//不希望用户创建一个 Pet 对象。
//只能定义 Pet 的指针或引用。
抽象类的子类必须覆盖抽象父类的所有成员方法才可以被实例化,就算只是用一个空的函数体进行覆盖。
抽象类不能同时用final和abstract修饰 < “不能被继承”与 “一定要被继承” >
抽象方法不能同时被static和abstract修饰 < “不用实例化即存在” 与“一定要继承才能实例化” >
抽象类的构造方法不能成为构造函数
接口
接口的定义:interface
[和普通的类一样只可选:default/public] interface name extends a,b{
//接口可以多继承,关键字仍然是extends
[public static final] 数据类型 变量名=常量值;
//系统默认所有的成员变量的修饰都是public static final
//一方面表明其不可被更改,另一方面有类要实现这个接口,可以直接当常量调用
[public abstract] 返回类型 方法名(参数列表);
//系统默认所有的成员方法的修饰都是public abstract
//接口只会包括常量定义和抽象方法,构造方法不能成为抽象方法,所以接口中不包括构造方法
}
接口的实现:implements
class name extends c implements a,b{
//一个类可以继承其他类,并实现多个接口
}
类必须要实现接口中的所有方法,即覆盖所有的抽象类
由于方法覆盖不能缩小父类方法的访问权限,而接口的成员内所有的方法都默认是public的访问权限,因此在实现这些方法时,必须设定访问权限也是public类型的,否则会产生访问权限错误
接口的作用:接口回调
通过声明一个接口类型,引用实现该接口的类的实例对象,进行多态实现接口中的各种方法
该接口同样不能访问实例对象的其他任何在接口中未定义的成员
特殊类
内部类
实名内部类
定义
[修饰符public/protected/default/private都行]
class name [extends...][implements...]{
}
创建对象:内部类有无static修饰
class Out {
static public class Inner1 {
static int x = 10;
int y = 20;
}
public class Inner2 {
static final int z = 10;
}
}
public class OutTest{
public static void main(string []args){
//有static修饰的内部类,可以不用先实例化外部类:
Out.Inner1 i = new Out.Inner1();
//无static修饰的内部类,必须先实例化外部类:new两下
Out.Inner2 j = new Out().new Inner2();
//等同于
Out o=new Out();
Out.Inner2 k=o.new Inner2();
//书上定义内部类的时候不需要带上“外部类.”
//但是编译器会报错
}
}
静态属性成员与非静态属性成员
书上说,非静态内部类的静态属性成员只能是静态成员变量而不能有静态成员方法,而且静态成员变量必须加上final修饰 ( 但是我的idea并不遵循这样的规则 )
System.out.println(Out.Inner1.x);//静态内部类的静态成员变量
System.out.println(Out.Inner2.z);//非静态内部类的静态成员变量
内部类的成员变量与外部类的成员变量同名的调用
class Out {
int x=100;
public class Inner {
int x = 10;
public void show(int x){
x++; //形参x
this.x++; //内部类的成员变量
Out.this.x++; //外部类的成员变量
}
}
}
匿名内部类
不具有类名的内部类,且不具有抽象和静态属性,也不能派生出子类
定义:带分号!!!!
new 父类名 (父类型的构造方法调用的参数列表){
类体
};
//匿名类好像都是在主函数里面才定义的
//抽象函数可以在主函数里的匿名内部类实现抽象函数的定义
//再进一步被调用
作用
- 实现多继承
- 避免父类和接口同名方法的冲突,即两个都想调用
- 隐藏类实现的细节
前面两点其实就是通过内部类的继承抽象类和实现接口,使得外部类包含了这些可能会被覆盖的方法,这样一来就可以通过构造内部类进行这些方法的调用了
最后一点体现在成员方法里,在成员方法中也可以包含一个内部类,然后返回该内部类作为多态的环节;内部类新增了prive和protected的修饰,可以更加灵活
数组
一维数组
在前文已有提及,这里不再赘述
二维数组
//C++ 中的二维数组
//直接初始化
int d[][5]={{1}};
//最多只能缺省一维数组的维度
//其实C++中也可以定义一维维度不相同的二维数组
int **i = new int *[5];
i[0] = new int[10]{1};
cout << i[0][0] << endl;//1
i[1] = new int[8]{1};
cout << i[1][0] << endl;//1
//java中的二维数组
//声明并直接初始化:
int[][] b = {{1, 2, 3}, {1, 2}, {4, 2, 5, 6};
//java里面直接初始化好像就是不可以在【】中写数字的
System.out.println(b[1][1]);
//2
//声明并使用new实例化:
int[][] a = new int[3][4];
//a[i][j]为默认值0
//还可以动态实例化:以引用数据类型为例
notNamed[][] c = new notNamed[3][];
//若缺省只能缺省一维数组的维度,此时c[i]为null
//可以理解为先分配更高维度数组的空间,然后这个空间指向低维的数组
c[0]=new notNamed[3];
c[1]=new notNamed[5];
c[2]=new notNamed[6];
//动态初始化:必须为每个数组元素单独分配空间,否则不能使用
c[0][0]=new notNamed(0);
c[0][1]=new notNamed(0);
c[0][2]=new notNamed(0);
//...
对于引用数据类型的二维数组,必须首先为最高维分配引用空间(由于只能用new初始化,所以最高维不可缺省),然后再顺次为低维分配空间,而且必须为每个数组元素单独分配空间,否则将不能使用。
注意事项
- 数组一旦创建后,不能调整大小,但可使用相同的引用变量来引用一个全新的数组
int[][] a = new int[3][4];
a=new int [4][5];
- 数组是一种特殊的引用对象,其长度可以用《数组名.length》来引用
notNamed[][] c = new notNamed[3][]; //c.length=3 c[0] = new notNamed[3]; c[1] = new notNamed[5]; c[2] = new notNamed[6]; //c.length[2]=6
字符串
在Java中 中文的单个汉字与英文的单个字母都是占4个字节
计算String字符串的长度的函数length()正是据此工作的
String与其他类型的转变
String 转化为基本数据类型
基本数据类型 提供的 Integer.parseInt()
基本数据类型转化为String
String 提供的 String.valueOf() [float=1342.6f转化不带f]
其他数据类型提供的toString()