JAVASE第六式


A、while(int i<7){i++;System.out.println("i is "+i);},int i要放在外面声明

B、int j=3;while(j){ System.out.println("j is "+j);},

java判断处为boolean类型,应该如j==3,j>1之类才行;不像C中,0可以表示假,非0可以表示真。java的真假就是true和false以及判别式。

C、int j=0;for(int k=0;j+k!=10;j++,k++){System.out.println("j is "+j+"k is" +k);},当j=5,k=5时循环结束

D、int j=0; do{System.out.println("j is "+j++);if(j==3){continue loop;}}while(j<10);,

continue loop前面没有标注出循环名loop,相当于未定义


泛型仅仅是java的语法糖,它不会影响java虚拟机生成的汇编代码,在编译阶段,虚拟机就会把泛型的类型擦除,还原成没有泛型的代码,顶多编译速度稍微慢一些,执行速度是完全没有什么区别的.


CGI(Common Gateway Interface),通用网关接口

通用网关接口,简称CGI,是一种根据请求信息动态产生回应内容的技术。通过CGI,Web 服务器可以将根据请求不同启动不同的外部程序,并将请求内容转发给该程序,在程序执行结束后,将执行结果作为回应返回给客户端。也就是说,对于每个请求,都要产生一个新的进程进行处理。因为每个进程都会占有很多服务器的资源和时间,这就导致服务器无法同时处理很多的并发请求。另外CGI程序都是与操作系统平台相关的,虽然在互联网爆发的初期,CGI为开发互联网应用做出了很大的贡献,但是随着技术的发展,开始逐渐衰落。

Servlet

Servlet最初是在1995年由James Gosling 提出的,因为使用该技术需要复杂的Web服务器支持,所以当时并没有得到重视,也就放弃了。后来随着Web应用复杂度的提升,并要求提供更高的并发处理能力,Servlet被重新捡起,并在Java平台上得到实现,现在提起Servlet,指的都是JavaServlet。Java Servlet要求必须运行在Web服务器当中,与Web服务器之间属于分工和互补关系。确切的说,在实际运行的时候Java Servlet与Web服务器会融为一体,如同一个程序一样运行在同一个Java虚拟机(JVM)当中。与CGI不同的是,Servlet对每个请求都是单独启动一个线程,而不是进程。这种处理方式大幅度地降低了系统里的进程数量,提高了系统的并发处理能力。另外因为Java Servlet是运行在虚拟机之上的,也就解决了跨平台问题。如果没有Servlet的出现,也就没有互联网的今天。
在Servlet出现之后,随着使用范围的扩大,人们发现了它的一个很大的一个弊端。那就是为了能够输出HTML格式内容,需要编写大量重复代码,造成不必要的重复劳动。为了解决这个问题,基于Servlet技术产生了JavaServet Pages技术,也就是JSP。Servlet和JSP两者分工协作,Servlet侧重于解决运算和业务逻辑问题,JSP则侧重于解决展示问题。Servlet与JSP一起为Web应用开发带来了巨大的贡献,后来出现的众多Java Web应用开发框架都是基于这两种技术的,更确切的说,都是基于Servlet技术的。

--------------------分割线-------------------------

Servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁。
而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于Servlet 。


A----------抽象类不一定含有抽象方法,接口中的方法都是抽象方法。

接口中的方法默认修饰符有public abstract。
B----------一个类只能继承一个一个抽象类,但可以实现多个接口;一个接口可以继承多个接口。
Java里类是单继承的,接口是可以多继承的,用关键字extends。
C----------抽象类和接口中的方法都没有方法体。
抽象类中的方法是可以有方法体的。JDK1.8之后,接口中的方法也可以有方法体,用default关键字修饰方法。
D----------抽象类可以含有私有成员变量,接口不含有私有成员变量。
接口中的成员变量都是public static final的,一般用作常量。


两种方法的区别:

1.start方法
用start方法来启动线程,是真正实现了多线程,通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法。但要注意的是,此时无需等待run()方法执行完毕,即可继续执行下面的代码。所以run()方法并没有实现多线程。

2.run方法

run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码

run方法是线程内重写的一个方法,start一个线程后使得线程处于就绪状态,
当jvm调用的时候,线程启动会运行run。run函数是线程里面的一个函数不是多线程的。

System.gc()只是通知系统,希望进行回收。但不能确定什么时候回收,甚至不知道会不会进行回收

所以,所有能明确操控gc的说法都是耍流氓


class Test {
    public static void main(String[] args) {
        System.out.println(new B().getValue());
    }
    static class A {
        protected int value;
        public A (int v) {
            setValue(v);
        }
        public void setValue(int value) {
            this.value= value;
        }
        public int getValue() {
            try {
                value ++;
                return value;
            } finally {
                this.setValue(value);
                System.out.println(value);
            }
        }
    }
    static class B extends A {
        public B () {
            super(5);
            setValue(getValue()- 3);
        }
        public void setValue(int value) {
            super.setValue(2 * value);
        }
    }
}

思考和解决这个题的主要核心在于对java多态的理解。个人理解时,

执行对象实例化过程中遵循多态特性 ==> 调用的方法都是将要实例化的子类中的重写方法,

只有明确调用了super.xxx关键词或者子类中没有该方法时才会去调用父类相同的同名方法

Step 1: new B()构造一个B类的实例

此时super(5)语句调用显示调用父类A带参的构造函数,该构造函数调用setValue(v),

这里有两个注意点一是虽然构造函数是A类的构造函数,但此刻正在初始化的对象是B的一个实例,

因此这里调用的实际是B类的setValue方法,于是调用B类中的setValue方法 ==>

而B类中setValue方法显示调用父类的setValue方法,将B实例的value值设置为2 x 5 = 10
紧接着,B类的构造函数还没执行完成,继续执行setValue(getValue()- 3) // 备注1语句

先执行getValue方法,B类中没有重写getValue方法,因此调用父类A的getValue方法。这个方法比较复杂,需要分步说清楚:

  1. 调用getValue方法之前,B的成员变量value值为10。
  2. value++ 执行后, B的成员变量value值为11,此时开始执行到return语句,将11这个值作为getValue方法的返回值返回出去
  3. 但是由于getValue块被try finally块包围,因此finally中的语句无论如何都将被执行,所以步骤2中11这个返回值会先暂存起来,到finally语句块执行完毕后再真正返回出去。
  4. 这里有很重要的一点:finally语句块中 this.setValue(value)方法调用的是B类的setValue方法。为什么?因为此刻正在初始化的是B类的一个对象(运行时多态),就像最开始第一步提到的一样(而且这里用了使用了this关键词显式指明了调用当前对象的方法)。因此,此处会再次调用B类的setValue方法,同上,super.关键词显式调用A的setValue方法,将B的value值设置成为了2 * 11 = 22
  5. 因此第一个打印项为22。
  6. finally语句执行完毕 会把刚刚暂存起来的11 返回出去,也就是说这么经历了这么一长串的处理,getValue方法最终的返回值是11。

回到前面标注了 //备注1 的代码语句,其最终结果为setValue(11-3)=>setValue(8)
而大家肯定也知道,这里执行的setValue方法,将会是B的setValue方法。 之后B的value值再次变成了2*8 = 16;

Step2: new B().getValue()

B类中没有独有的getValue方法,此处调用A的getValue方法。同Step 1,

  1. 调用getValue方法之前,B的成员变量value值为16
  2. value++ 执行后, B的成员变量value值为17,此时执行到return语句,会将17这个值作为getValue方法的返回值返回出去
  3. 但是由于getValue块被try finally块包围而finally中的语句无论如何都一定会被执行,所以步骤2中17这个返回值会先暂存起来,到finally语句块执行完毕后再真正返回出去。
  4. finally语句块中继续和上面说的一样: this.setValue(value)方法调用的是B类的setValue()方法将B的value值设置成为了2 * 17 = 34
  5. 因此第二个打印项为34。
  6. finally语句执行完毕 会把刚刚暂存起来的17返回出去。
  7. 因此new B().getValue()最终的返回值是17.

Step3: main函数中的System.out.println

将刚刚返回的值打印出来,也就是第三个打印项:17

最终结果为 22 34 17。 如果朋友们在看的过程中仍然有疑问,可以亲自把代码复制进去ide,在关键语句打下断点,查看调用方法的对象以及运行时的对象值,可以有更深刻的理解。



因为Test类的hello方法是静态的,所以是属于类的,当实例化该类的时候,静态会被优先加载而且只加载一次,所以不受实例化new Test();影响,只要是使用到了Test类,都会加载静态hello方法!
另外,在其他类的静态方法中也是可以调用公开的静态方法,此题hello方法是使用public修饰的所以在MyApplication中调用hello也是可以的。
总结:即使Test test=null;这里也会加载静态方法,所以test数据中包含Test类的初始化数据。(静态的,构造的,成员属性)
因此test.hello是会调用到hello方法的。


标识符由数字和英文字符以及特殊字符(_ 和 $)组成;其中又必须满足不能和关键字重复、不能以数字开头。


阿里规范里,数组严禁使用int a[][] 等形式,应当使用int[][] a 的可读性较强的形式



情况1: try{} catch(){}finally{} return; 显然程序按顺序执行。

情况2: try{ return; }catch(){} finally{} return;
先执行try块中return 语句(包括return语句中的表达式运算),但不返回;
执行finally语句中全部代码
最后执行try中return 返回
finally块之后的语句return不执行,因为程序在try中已经return。


情况3: try{ } catch(){return;} finally{} return;
1、程序先执行try,如果遇到异常执行catch块,


有异常:
执行catch中return语句,但不返回
执行finally语句中全部代码,
最后执行catch块中return返回 finally块后的return语句不再执行。


无异常:执行完try再finally再return…


情况4: try{ return; }catch(){} finally{return;}
执行try块return语句(包括return语句中的表达式运算),但不返回;
再执行finally块
执行finally块,有return,从这里返回
此时finally块的return值,就是代码执行完后的值

情况5: try{} catch(){return;}finally{return;}
程序执行catch块中return语句(包括return语句中的表达式运算),但不返回
再执行finally块,
执行finally块,有return,从这里返回。


情况6: try{ return;}catch(){return;} finally{return;}
1、程序执行try块中return语句(包括return语句中的表达式运算),但不返回;

有异常:
执行catch块中return语句(包括return语句中的表达式运算),但不返回;
再执行finally块
执行finally块,有return,从这里返回。


无异常:
再执行finally块

执行finally块,有return,从这里返回


程序入口,public static void main(String[] args){}也是方法,

方法内部的变量是局部变量,局部变量必须初始化,但是全局变量不需要初始化


值类型:基本数据类型(int、double、boolean ...)
引用类型:reference类型,通过它可以直接或间接的查找到对象在java堆中数据存放的起始地址或索引(对象在java堆中的哪个位置)

先来两个例子,变量的赋值在内存中是怎么样的:

先上一张运行时数据区的网图

局部变量表、操作数栈在虚拟机栈中

一:基本变量的赋值

public void method() {
    int a = 1;
    int b = a;
}

该方法的字节码:
    0 iconst_1  // 将 int 型 1 推送至操作数栈栈顶
    1 istore_1  // 将操作数栈栈顶 int 型值存入第二个本地变量
    2 iload_1   // 将第二个int型本地变量推送至操作数栈顶
    3 istore_2  // 将操作数栈栈顶 int 型值存入第三个本地变量
    4 return    // 方法返回

二:基本变量的赋值

publicvoidmethod2() {
    Object a = newObject();
    Object b = a;
}
 
该方法的字节码:
    0new#15<java/lang/Object> // 创建一个对象,并将其引用压入栈顶
    3dup  // 复制栈顶引用类型数据并将其值压入栈顶
    4invokespecial #1<java/lang/Object.<init>>  // 执行对象构造方法(使用了栈顶引用类型)
    7astore_1  // 将操作数栈栈顶引用类型数据值存入第二个本地变量 a
    8aload_1   // 将第二个引用类型本地变量推送至栈顶
    9astore_2  // 将栈顶引用类型数值存入第三个本地变量b
    10return   // 方法返回

再看选项

  • A(✔):值类型的和引用类型的变量赋值就像上面一样,先把局部变量表中 a 的数值存入操作数栈,再取出来放入 b,不过区别在于:
    • 前者在局部变量表中存放的数值是真正意义上的 a 代表的数据,所以说赋值操作是“复制(iload_1、istore_2)”一份出来再给 b
    • 后者在局部变量表中存放的数值不是真正意义上 a 代表的数据,只是指向堆中对象的指针(指向 a 真正代表的数据)。赋值操作只是把 a 中数值即指针复制给 b ,堆中a代表正真的数据即对象(0x11)并没有被复制
  • B(✔):
    • "值类型数据是在栈(指虚拟机栈)上分配内存空间,它的变量直接包含变量的实例,使用效率相对较高。" :局部变量表(在虚拟机栈中)中存放数值的就是真正意义上的数据,不用访问堆,效率较高
    • “而引用类型数据是分配在上,引用类型的变量通常包含一个指向实例(指向堆中对象)的指针,变量通过指针来引用实例。” :局部变量表中存放的数值是指向中数据(对象)的指针
  • C(✔):类可以继承,基本数据类型不能继承
  • D(X):“值类型变量的作用域主要是在栈上分配内存空间内”:如果是跟上诉一致在方法内定义的基本变量,它的作用域就在栈中;如果是在类中定义的成员变量例如 Class A 中有成员变量 int val,这个类的实例在堆中,那么 val 作用域也在堆中

题外话:操作数栈深度在编译期就已经决定


A.HashMap不能保证元素的顺序,而LinkedHashMap可以保持数据的插入顺序,TreeMap可以按照键值进行排序(可自定比较器)

B.HashMap允许存在多条null值

C.HashMap允许且仅允许一条null键

D.Map就是通过键/值形式保存数据的



重载:类中可以创建多个方法,它们具有相同的名字,但具有不同的参数和不同的定义。调用方法时通过传递给它们的不同参数个数和参数类型给它们的不同参数个数和参数类型给它们的不同参数个数和参数类型来决定具体使用哪个方法, 这就是多态性。

重写Overriding是父类与子类之间多态性的一种表现,在子类中定义某方法与其父类有相同的名称和参数

重载Overloading是一个类中多态性的一种表现。

重载的时候,方法名要一样,但是参数类型和个数不一样,返回值类型可以相同也可以不相同。

无法以返回型别作为重载函数的区分标准。而且编译的时候只有返回值不同,会报错。及使不报错,运行时无法区分调用哪个方法


A是抽象方法,抽象类可以包含抽象方法,也可以不包含,实现重载。(√)

B 在类中不能constInt = constInt + 5(×)

C 返回值不能作为重载的依据(×) D 有方法体的不能作为抽象函数(×)


线程安全的map:HashTable,SynchronizedMap,ConcurrentHashMap

Hashtable是线程安全的哈希表,它是通过synchronized来保证线程安全的;即,
多线程通过同一个“对象的同步锁”来实现并发控制。Hashtable在线程竞争激烈时,
效率比较低(此时建议使用ConcurrentHashMap)。
当一个线程访问Hashtable的同步方法时,其它线程如果也在访问Hashtable的同步方法时,
可能会进入阻塞状态。

Collections.synchronizedMap()使用了synchronized同步关键字来保证对Map的操作是线程安全的。

ConcurrentHashMap是线程安全的哈希表。在JDK1.7中它是通过“锁分段”来保证线程安全的,
本质上也是一个“可重入的互斥锁”(ReentrantLock)。多线程对同一个片段的访问,是互斥的;
但是,对于不同片段的访问,却是可以同步进行的。在JDK1.8中是通过使用CAS原子更新、volatile关键字、
synchronized可重入锁实现的。

数组是一种引用数据类型 那么他肯定是继承Object类的

所以里面有equals() 方法 但是肯定没有重写过 因为他并不是比较数组内的内容

使用Arrays.equals() 是比较两个数组中的内容。


散列表之开放定址法 在前面的文章中我们介绍过《散列表之链接法》,在
链接法中,如果不同键值却将有相同的映射值,即有不同键值的元素却映射到散列表中的同一位置,那
么就采用链表的方法,将映射到同一位置的元素插入到同一个链表之中,
当需要删除, 查询元素时,只需要遍历该链表即可,链接法在最坏情况下删除和查询元素的
时间代价为O(n)O(n) 
今天我们来讲散列表中另外一种解决冲突的方法,那就是开放定址法(open addressing)。 
假如你在外面旅游时,吃坏东西,急需上厕所,当你好不容易找到一件洗手间的时候,发现排了好多人,
这时你会怎么做? 
如果是链接法:排队不就行了,我就在外面等,迟早会排到我的 
如果是开放定址法:直接放弃现有厕所,去寻找新的厕所 没错,放弃已被占用的位置,寻找新的插入位置
就是开放定址法的思想,

开放定址法中的开放二字指的是没有被占用的位置,定址指的是确定位置。
开放定址法中,所有的元素都放在散列表中(链接法放在链表中)。也就是说散列表中的每一个位置,要么有元素
要么没有元素。
当需要删除,查询元素时,我们从某一个位置开始,按照某种特定的确定下一个位置的方法来检查所有表项
直到找到目标元素,或者没有找到。

Java语言中的异常处理包括声明异常、抛出异常、捕获异常和处理异常四个环节。

throw用于抛出异常。

throws关键字可以在方法上声明该方法要抛出的异常,然后在方法内部通过throw抛出异常对象。

try是用于检测被包住的语句块是否出现异常,如果有异常,则抛出异常,并执行catch语句。

cacth用于捕获从try中抛出的异常并作出处理

finally语句块是不管有没有出现异常都要执行的内容。

throws关键字声明抛出的异常 throw用于抛出异常对象

throw关键字是抛出一个异常类对象,在该方法上声明要抛出的使用的是throws


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值