-
实体类保存之后怎样接收到自增列的值_怎样管理对象
2021-01-28 17:54:45有一天晚上我脑海中突然冒出来...小弈是刚工作时的我,他说:通过 new 来创建一个对象然后直接使用就好了啊。publicclassHelloWorld{publicvoidhello(){System.out.println("helloworld!");}}HelloWorldhelloWorld=...有一天晚上我脑海中突然冒出来一个问题:“怎样管理我们代码中的对象”。
小弈是刚工作时的我,他说:通过 new 来创建一个对象然后直接使用就好了啊。
public class HelloWorld {
public void hello() {
System.out.println("hello world!");
}
}
HelloWorld helloWorld = new HelloWorld();
helloWorld.hello();你们看,我有一个 HelloWorld 类,我用 new 就能直接创建一个对象,然后就能使用这个对象中所有的方法了,多简单啊。
二弈是工作两年的我,他一脸鄙视的对小弈说,你别整天 HelloWorld 好不好,还有啊,除了 new 你就不会其他的了,能不能有点追求啊?
小弈对二弈说那你说除了 new 还有什么办法啊?
二弈说可以通过 Class 的 newInstance 或者 Constructor 的 newInstance 来创建对象实例啊。
不过你得记住,Class 的 newInstance 只能对那些拥有可见的(Accessible)无参构造函数的类,才能进行对象的实例化,而 Constructor 就没有这些限制。
大弈是工作三年的我,他说,虽然你们的方法都可以用来创建对象,但都还是手动创建的,太原始了,生产力太低。
工欲善其事,必先利其器,我们也得找个高效的生产力工具。IOC 容器你们了解吧?
以前我们在一个对象中如果要调用另外一个对象的方法时,都是通过 new 或者反射来手动创建该对象,但是每次都这样做太累了,并且类之间的耦合也很高。
通过 IOC 容器,我们可以把所有的对象交给容器来管理,在使用之前只需要定义一下对象,然后再使用到该对象时,IOC 容器就会帮我们把该对象初始化好,这样是不是更方便呢?
大弈说完,举了一个例子:
@Bean
public class RegisterService {
public void register() {
// do register
}
}
@Bean
public class LoginService {
public void login() {
// do login
}
}
@Bean
public class HelloWorld {
@Autowired
private RegisterService registerService;
@Autowired
private LoginService loginService;
public void hello() {
// 注册
registerService.register();
// ...
// 登录
loginService.login();
}
}IOC 容器通过一种叫 Bean 的注解,在系统启动时扫描所有通过 Bean 标注的类,对这些类进行实例化,然后将所有的对象都保存在容器中。再扫描所有通过 Autowired 标注的属性或者方法,从容器中找到与之匹配(通过名称或者类型等)的对象将具体的对象赋值给这些属性。这样我们就可以直接将这些对象拿来使用了,作为一个伸手党是不是很幸福啊。
老弈是工作五年的我,他听了大弈的话之后,提出了一个问题,对于新的项目可以使用这种 IOC 的容器,可是对于那些遗留的老项目来说,要使用 IOC 来改造代价会比较大,不太符合实际。
比如在一个遗留的老项目中,有一个核心的接口 Handler:
public interface Handler<REQ, RES> {
RES handle(REQ request);
}Handler 接口有很多的实现类,我们需要对不同的请求来调用不同的 Handler 实现类进行处理,如果用 IOC 容器来管理这些实现类,显然不太合适,因为我们处理之前是不知道该用哪个 Handler 实现类的。
大弈想了想,如果 Handler 接口只有几个固定的实现类,并且在使用时只会使用一个来进行处理,那么倒是可以在启动前通过配置的方式来确定具体使用哪种 Handler ,比如可以通过 @Conditional 根据某些条件来确定加载具体的对象,但是这种要在使用时才能确定 Handler 对象的类型确实比较棘手。
老弈看大家都不说话了,就继续说了下去。
为了要在调用方法时使用不同的 Handler 来处理不同的而请求,需要确定两种类,一种是请求类,一种是处理类,并且要让请求类和处理类一一对应起来。
假设我们的请求类是一个 Packet 类,每一个具体的请求类都继承自这个基类。
那么想要确定每一个具体的 Packet 是什么类型的,可以有很多种方法,可以为每个 Packet 取一个唯一的名字,例如:
public abstract class Packet {
public abstract String name();
}也可以为每一个 Packet 指定一个标志,例如:
public abstract class Packet {
public abstract int symbol();
}但是不管哪种方式,每一个 Packet 的实现类都需要实现抽象类中的方法,来“标志”自己是哪种 Packet。
我们以第二种方式举例,假设我们有两个具体的 Packet:
public class RegisterPacket extends Packet {
// 注册所需要的其他参数
int symbol() {
return 1;
}
}
public class LoginPacket extends Packet {
// 登录所需要的其他参数
int symbol() {
return 2;
}
}这样当我们接收到 request 对象时,通过调用 request.symbol() 就知道这个 request 是哪种类型的 Packet 了,这时只要找到具体的 Handler 实现类来处理就可以了。
那请求类已经可以确定了,怎样确定 Handler 处理类呢?我们是否也可以在 Handler 接口中定义一个 symbol 方法呢,像这样:
public interface Handler<REQ, RES> {
int symbol();
RES handle(REQ request);
}这样的话,只要在所有的实现类中实现 symbol 方法来标注该 Handler 是用来处理何种 request 的即可。
public RegisterHandler implements Handler {int symbol(){return 1;
}RES handle(RegisterPacket request){// 具体的处理方法
}
}public LoginHandler implements Handler {int symbol(){return 2;
}RES handle(LoginPacket request){// 具体的处理方法
}
}最后把所有的 Handler 实现类都实例化后保存在一个 HandlerProvider 中,要使用时再到 HandlerProvider 中来获取即可:
public interface HandlerProvider {
Handler getHandler(int symbol);
}那怎样获取到所有的 Handler 的实现类呢,有两种方法。
一种是通过 ServiceLoader.load(Handler.class) 的方式来获取,不过这种通过 spi 的方式需要在项目的 resources/META-INF/services/ 目录下创建一个 xxx.Handler 的文件,并在文件中将所有 Handler 的实现类的完全类限定符列出来。
另一种比较简单的方式是通过扫描的方式,获取到所有 Handler 的实现类。
到现在为止,我们的实现还算可以,但是有一个问题,那就是在 Handler 接口中我们增加了一个方法,这样做就对原来的代码进行了侵入。
为了让原来的代码保持不变,我们可以定义一个注解来标注在所有的 Handler 实现类上,比如这样:
@Symbol(1)
public RegisterHandler implements Handler {RES handle(RegisterPacket request){// 具体的处理方法
}
}@Symbol(2)public LoginHandler implements Handler {RES handle(LoginPacket request){// 具体的处理方法
}
}这样就将 Handler 的实现和标注进行了解耦了,也可以通过扫描 @Symbol 注解来获取到所有的 Handler 实现类,不过这样做的缺点就是假如我忘记对某个 Handler 实现类添加 @Symbol 注解,到时候就获取不到该 Handler 了。
大家听完老弈的话之后,都陷入了沉思,我靠,还可以这么玩,真有趣。
这时候现在的我,也就是逅弈,说了一句,如果我有一个接口,他只有几个固定的实现类,我不想搞那一套那么重的实现方式,但是我也需要动态的获取实现类来对请求进行处理,那我该怎么办呢?
比如我有一个序列化的接口,如下所示:
public interface Serializer {
byte[] serialize(Packet packet);
}然后只有五种具体的序列化的实现类,如下所示:
public class JdkSerializer implements Serializer {
@Override
public byte[] serialize(Packet packet) {
// 具体的序列化操作
}
}
public class FastJsonSerializer implements Serializer {
@Override
public byte[] serialize(Packet packet) {
// 具体的序列化操作
}
}
public class HessianSerializer implements Serializer {
@Override
public byte[] serialize(Packet packet) {
// 具体的序列化操作
}
}
public class KryoSerializer implements Serializer {
@Override
public byte[] serialize(Packet packet) {
// 具体的序列化操作
}
}
public class ProtoStuffSerializer implements Serializer {
@Override
public byte[] serialize(Packet packet) {
// 具体的序列化操作
}
}那么我们该怎么确定使用哪种序列化方式对参数 packet 进行序列化呢?
使用老弈刚刚说的那一套也确实能够实现,不过太麻烦了,又得对 Packet 定义 symbol,又得对 Hander 实现类进行标注,还得扫描所有的实现类。
我只有五个实现类,不需要搞那么麻烦的。
其实很简单,只需要定义一个枚举类,表示序列化的算法,然后对 Packet 增加一个 algorithm 方法用来表示,使用何种序列化算法,如下所示:
public enum SerializeAlgorithm {
JDK((byte) 1),
FAST_JSON((byte) 2),
HESSIAN((byte) 3),
KRYO((byte) 4),
PROTO_STUFF((byte) 5);
private byte type;
SerializeAlgorithm(byte type) {
this.type = type;
}
}
public abstract class Packet implements Serializable {
public abstract byte algorithm();
}然后定义一个 SerializerChooser 根据不同的算法选择不同的 Serializer 实现类即可:
public interface SerializerChooser {
Serializer choose(byte algorithm);
}因为根据算法是可以知道对应的序列化接口的,所以就没有必要去扫描了,直接把几种序列化的实现类枚举出来即可,对象的实例可以使用单例模式,如下所示:
public class DefaultSerializerChooser implements SerializerChooser {
private DefaultSerializerChooser() {
}
public static SerializerChooser getInstance() {
return Singleton.get(DefaultSerializerChooser.class);
}
@Override
public Serializer choose(byte algorithm) {
SerializeAlgorithm serializeAlgorithm = SerializeAlgorithm.getEnum(algorithm);
switch (serializeAlgorithm) {
case JDK: {
return Singleton.get(JdkSerializer.class);
}
case FAST_JSON: {
return Singleton.get(FastJsonSerializer.class);
}
case HESSIAN: {
return Singleton.get(HessianSerializer.class);
}
case KRYO: {
return Singleton.get(KryoSerializer.class);
}
case PROTO_STUFF: {
return Singleton.get(ProtoStuffSerializer.class);
}
default: {
return null;
}
}
}
}我说完后,大家又一次陷入了沉思,我知道大家都在思考,他们会在每一次思考中获得进步和成长,正如我在思考后得到成长一样。
小鸟总有一天会成长为老鸟,我还走在成长的路上。
推荐阅读(点击即可跳转阅读)
1.SpringBoot内容聚合
2.面试题内容聚合
3.设计模式内容聚合
4.Mybatis内容聚合
5.多线程内容聚合
觉得不错?欢迎转发分享给更多人
我知道你 “在看”
-
js创建json对象_如何创建一个微信小程序?
2020-12-02 17:52:23微信小程序(Mini Program)是一种运行在微信内部,程序大小一般不超过2MiB(最高不超过8MiB)。正是因为这种特性,微信小程序也被称为是一种不需要下载安装...二、创建项目打开开发程序之后先扫码登录,之后在左侧栏...微信小程序(Mini Program)是一种运行在微信内部,程序大小一般不超过2MiB(最高不超过8MiB)。正是因为这种特性,微信小程序也被称为是一种不需要下载安装即可使用的应用。那么作为开发者,我们又怎样开发出这种简单轻巧的小程序呢?
一、准备工具
这里废话不多说,进入微信小程序开发工具下载界面,根据自己的操作系统,建议选择下载稳定版。
二、创建项目
打开开发程序之后先扫码登录,之后在左侧栏目选择小程序并点击右侧带有加号的白色方框。
项目名称和目录可以随意修改。因为我们目前不需要对AppID进行发布,所以AppID选择使用测试号。其它缺省设置不必修改,直接点击右下角“创建”即可。
三、程序开发
项目创建成功之后,窗口会类似下图三部分:工具栏、模拟器和编辑器。
这三部分主要功能就不再详细说明。
四、代码构架
观察编辑器左侧目录列表,将所有文件夹展开会看到下图:
下面主要说明这些文件的作用以及协同关系。
pages文件夹下存放的文件夹是页面,也就是每一个页面在pages文件夹中都是一个文件夹,而页面名称就是这个文件夹的名称。如上图,pages下有2个文件夹分别名为index和logs,这就表明这个小程序有两个页面。
每一个页面文件夹下都有4个和文件夹同名的不同类型文件,他们分别是
.json 后缀的 JSON 配置文件
.wxml 后缀的 WXML 模板文件
.wxss后缀的 WXSS 样式文件
.js 后缀的 JS 脚本逻辑文件
接下来我们分别看看这4种文件的作用。
JSON 配置
JSON是一种数据格式,并不是编程语言,他的作用就像我们手机电脑中的设置一样,把我们手中的东西变为我们想要的样子。
以app.json为例,app.json 文件用来对微信小程序进行全局配置,他声明了小程序的所有页面路径、界面表现、网络超时时间、底部 tab 等。每一个小程序页面也可以使用同名.json 文件来对本页面的窗口表现进行配置,页面中配置项会覆盖 app.json 中相同的配置项。
WXML 模板
WXML与HTML相像,HTML 是用来描述网页的结构,所以在微信小程序中,WXML 充当的就是类似 HTML 的角色。WXML能够在屏幕上显示它本身所呈现的内容,但是在WXML写了一个按钮,我们希望用户在点击他的时候他能正确交互,这时就需要用到JS逻辑交互。
JS 逻辑交互
就像上面所说,一个服务仅仅只有界面展示是不够的,还需要和用户做交互:响应用户的点击、获取用户的位置等等。在小程序里边,我们就通过编写JS 脚本文件来处理用户的操作。另外,我们页面中需要用到的变量和函数方法也需要在JS文件中定义。有了它,再配合WXML,就能够写出来一个有模有样的程序了。
WXSS 样式
WXSS 具有 CSS 大部分的特性,但小程序在 WXSS 也做了一些扩充和修改。通俗点讲WXSS的作用就是定义WXML中我们所显示在屏幕上的那些按钮图片文字的高度宽度大小颜色等性质。和前边app.json, page.json 的概念相同,wxss也提供了全局的样式和局部样式。你可以写一个app.wxss 作为全局样式,会作用于当前小程序的所有页面,局部页面样式 page.wxss 仅对当前页面生效。
说完了4种主要文件,我们会发现在根目录下存在一个名为app.js的文件。其实和前边app.json, page.json 的概念相同,app.js定义了全局样式,同样也会作用于当前小程序的所有页面,局部页面样式page.js仅对当前页面生效。
工具配置 project.config.json
通常大家在使用一个工具的时候,都会针对各自喜好做一些个性化配置,例如界面颜色、编译配置等等,当你换了另外一台电脑重新安装工具的时候,你还要重新配置。
考虑到这点,小程序开发者工具在每个项目的根目录都会生成一个 project.config.json,你在工具上做的任何配置都会写入到这个文件,当你重新安装工具或者换电脑工作时,你只要载入同一个项目的代码包,开发者工具就自动会帮你恢复到当时你开发项目时的个性化配置,其中会包括编辑器的颜色、代码上传时自动压缩等等一系列选项。
sitemap 配置
根目录下sitemap.json声明了小程序及其页面是否允许被微信索引,文件内容为一个JSON对象,如果没有 sitemap.json ,则默认为所有页面都允许被索引。这个文件目前我们用不到,就先搁置不用考虑这个文件。
最后就是utils文件夹以及其中的util.js文件,和sitemap配置一样,我们也先搁置不考虑这个文件。
总结
到这里,我们就明白如何创建微信小程序,并对其中的文件逻辑和框架有了大致的了解。但是我们具体如何去构建代码,写出自己想要的小程序,这个以后再更吧。
-
005第1章 对象导论1.10对象的创建和生命期、1.11异常处理:处理错误、1.12并发编程
2019-05-24 18:37:421.10 对象的创建和生命期 在使用对象时,最关键的问题之一便是它们的生成和销毁方式。 每个对象为了生存都需要资源,尤其是内存。 当我们不再需要一个对象时,它必须被清理掉,使其占有的资源可以被释放和重用。 ...1.10 对象的创建和生命期
在使用对象时,最关键的问题之一便是它们的生成和销毁方式。
每个对象为了生存都需要资源,尤其是内存。
当我们不再需要一个对象时,它必须被清理掉,使其占有的资源可以被释放和重用。
空中交通管理系统、
怎样才能知道何时销毁这些对象呢?
当处理完某个对象之后,系统某个其他部分可能还在处理它。
在其他许多场合中也会遇到同样的问题,在必须明确删除对象的编程系统中(例如C++),此问题会变得十分复杂。
对象的数据位于何处?怎样控制对象的生命周期?
C++认为效率控制是最重要的议题,所以给程序员提供了选择的权力。牺牲了灵活性
第二种方式是在被称为堆(heap)的内存池中动态地创建对象。
在这种方式中,直到运行时才知道需要多少对象,它们的生命周期如何,以及它们的具体类型是什么。
这些问题的答案只能在程序运行时相关代码被执行到的那一刻才能确定。
动态方式有这样一个一般性的逻辑假设:对象趋向于变得复杂,所以查找和释放存储空间的开销不会对对象的创建造成重大冲击。
动态方式所带来的更大的灵活性正是解决一般化编程问题的要点所在。Java完全采用了动态内存分配方式。
每当想要创建新对象时,就要使用new关键字来构建此对象的动态实例。
还有一个议题?就是对象生命周期。对于允许在堆栈上创建对象的语言,编译器可以确定对象存活的时间,并可以自动销毁它。
在像C++这样的语言中,必须通过编程方式来确定何时销毁对象,这可能会因为不能正确处理而导致内存泄漏(这在C++程序中是常见的问题)
Java提供了被称为“垃圾回收器”的机制,它可以自动发现对象何时不再被使用,并继而销毁它。
垃圾回收器非常有用,因为它减少了所必须考虑的议题和必须编写的代码口,
更重要的是,垃圾回收器提供了更高层的保障,可以避免暗藏的内存泄漏问题,这个问题已经使许多C++项目折戟沉沙。
Java的垃圾回收器被设计用来处理内存释放问题(尽管它不包括清理对象的其他方面)。
垃圾回收器“知道”对象何时不再被使用,并自动释放对象占用的内存。
这一点同所有对象都是继承自单根基类Object以及只能以一种方式创建对象(在堆上创建)这两个特性结合起来,
使得用Java编程的过程较之用C++编程要简单得多,所要做出的决策和要克服的障碍也要少得多。1.11 异常处理:处理错误
自从编程语言问世以来,错误处理就始终是最困难的问题之一。
因为设计一个良好的错误处理机制非常困难
异常处理将错误处理直接置于编程语言中,有时甚至置于操作系统中。
异常是一种对象,它从出错地点被“抛出”,并被专门设计用来处理特定类型错误的相应的异常处理器“捕获”。
异常处理就像是与程序正常执行路径并行的,在错误发生时执行的另一条路径。
因为它是另一条完全分离的执行路径,所以它不会干扰正常的执行代码。
这往往使得代码编写变得简单,因为不需要被迫定期检查错误。
此外,被抛出的异常不像方法返回的错误值和方法设置的用来表示错误条件的标志位那样可以被忽略。
异常不能被忽略,所以它保证一定会在某处得到处理。
最后需要指出的是:异常提供了一种从错误状况进行可靠恢复的途径。
现在不再是只能退出程序,你可以经常进行校正,并恢复程序的执行,这些都有助于编写出更健壮的程序。Java的异常处理在众多的编程语言中格外引人注目,因为Java一开始就内置了异常处理,而且强制你必须使用它。
它是唯一可接受的错误报告方式。
如果没有编写正确的处理异常的代码,那么就会得到一条编译时的出错消息。
这种有保障的一致性有时会使得错误处理非常容易。值得注意的是,异常处理不是面向对象的特征——尽管在面向对象语言中异常常被表示成为一个对象。
异常处理在面向对象语言出现之前就已经存在了。1.12 并发编程
在计算机编程中有一个基本概念,就是在同一时刻处理多个任务的思想。
许多程序设计问题都要求,程序能够停下正在做的工作,转而处理某个其他问题,然后再返回主进程。
最初,主进程的挂起是通过硬件中断来触发的。
但是对于大量的其他问题于我们只是想把问题切分成多个可独立运行的部分(任务),从而提高程序的响应能力。
在程序中,这些彼此独立运行的部分称之为线程,上述概念被称为”并发”。
并发最常见的例子就是用户界面。通过使用任务,用户可以在揿[qìn]下按钮后快速得到一个响应,而不用被迫等待到程序完成当前任务为止。
通常,线程只是一种为单一处理器分配执行时间的手段。
所有这些都使得并发看起来相当简单,但是有一个隐患:共享资源。
如果有多个并行任务都要访问同一项资源,那么就会出问题。
例如,两个进程不能同时向一台打印机发送信息。
为了解决这个问题,可以共享的资源,例如打印机,必须在使用期间被锁定。
因此,整个过程是:某个任务锁定某项资源令完成其任务,然后释放资源锁,使其他任务可以使用这项资源。Java的并发是内置于语言中的,Java SE5已经增添了大量额外的库支持。
以下为原书原文1.10 对象的创建和生命期
在使用对象时,最关键的问题之一便是它们的生成和销毁方式。
每个对象为了生存都需要资源,尤其是内存。
当我们不再需要一个对象时,它必须被清理掉,使其占有的资源可以被释放和重用。
在相对简单的编程情况下,怎样清理对象看起来似乎不是什么挑战:你创建了对象,根据需要使用它,然后它应该被销毁。
然而,你很可能会遇到相对复杂的情况。例如,假设你正在为某个机场设计空中交通管理系统(同样的模型在仓库货柜管理系统、录像带出租系统或宠物寄宿店也适用)。
一开始问题似乎很简单:创建一个容器来保存所有的飞机,然后为每一架进入空中交通控制区域的飞机创建一个新的飞机对象,并将其置于容器中。
对于清理工作,只需在飞机离开此区域时删除相关的飞机对象即可。但是,可能还有别的系统记录若有关飞机的数据,也许这些数据不需要像主要控制功能那样立即引人注意。
例如,它可能记录着所有飞离机场的小型飞机的飞行计划。
因此你需要有第二个容器来存放小型飞机,无论何时,只要创建的是小型飞机对象,那么它同时也应该置入第二个容器内。
然后某个后台进程在空闲时对第二个容器内的对象进行操作。现在问题变得更困难了:怎样才能知道何时销毁这些对象呢?
当处理完某个对象之后,系统某个其他部分可能还在处理它。
在其他许多场合中也会遇到同样的问题,在必须明确删除对象的编程系统中(例如C++),此问题会变得十分复杂。对象的数据位于何处?怎样控制对象的生命周期?
C++认为效率控制是最重要的议题,所以给程序员提供了选择的权力。
为了追求最大的执行速度,对象的存储空间和生命周期可以在编写程序时确定,这可以通过将对象置于堆栈
(它们有时被称为自动变量(automatic variable)或限域变址(scoped variable))或静态存储区域内来实现。
这种方式将存储空间分配和释放置于优先考虑的位置,某些情况下这样控制非常有价值。
但是,也牺牲了灵活性,因为必须在编写程序时知道对象确切的数量、生命周期和类型。
如果试图解决更一般化的问题,例如计算机辅助设计、仓库管理或者空中交通控制,这种方式就显得过于受限了。第二种方式是在被称为堆(heap)的内存池中动态地创建对象。
在这种方式中,直到【运行时】才知道需要多少对象,它们的生命周期如何,以及它们的具体类型是什么。
这些问题的答案只能在程序运行时相关代码被执行到的那一刻才能确定。
如果需要一个新对象,可以在需要的时刻直接在堆中创建。
因为存储空间是在运行时被动态管理的,所以需要大量的时间在堆中分配存储空间,这可能要远远大于在堆栈中创建存储空间的时间。
在堆栈中创建存储空间和释放存储空间通常各需要一条汇编指令即可,分别对应将栈顶指针向下移动和将栈顶指针向上移动。
创建堆存储空间的时间依赖于存储机制的设计。动态方式有这样一个一般性的逻辑假设:对象趋向于变得复杂,所以查找和释放存储空间的开销不会对对象的创建造成重大冲击。
动态方式所带来的更大的灵活性正是解决一般化编程问题的要点所在。Java完全采用了动态内存分配方式。
每当想要创建新对象时,就要使用new关键字来构建此对象的动态实例。还有一个议题?就是对象生命周期。对于允许在堆栈上创建对象的语言,编译器可以确定对象存活的时间,并可以自动销毁它。
然而,如果是在堆上创建对象,编译器就会对它的生命周期一无所知。
在像C++这样的语言中,必须通过编程方式来确定何时销毁对象,这可能会因为不能正确处理而导致内存泄漏(这在C++程序中是常见的问题)
Java提供了被称为“垃圾回收器”的机制,它可以自动发现对象何时不再被使用,并继而销毁它。
垃圾回收器非常有用,因为它减少了所必须考虑的议题和必须编写的代码口,
更重要的是,垃圾回收器提供了更高层的保障,可以避免暗藏的内存泄漏问题,这个问题已经使许多C++项目折戟沉沙。Java的垃圾回收器被设计用来处理内存释放问题(尽管它不包括清理对象的其他方面)。
垃圾回收器“知道”对象何时不再被使用,并自动释放对象占用的内存。
这一点同所有对象都是继承自单根基类Object以及只能以一种方式创建对象(在堆上创建)这两个特性结合起来,
使得用Java编程的过程较之用C++编程要简单得多,所要做出的决策和要克服的障碍也要少得多。1.11 异常处理:处理错误
自从编程语言问世以来,错误处理就始终是最困难的问题之一。
因为设计一个良好的错误处理机制非常困难,所以许多语言直接略去这个问题,将其交给程序库设计者处理,
而这些设计者也只是提出了一些不彻底的方法,这些方法可用于许多很容易就可以绕过此问题的场合,而且其解决方式通常也只是忽略此问题。
大多数错误处理机制的主要问题在于,它们都依赖于程序员自身的警惕性,这种警惕性来源于一种共同的约定于而不是编程语言所强制的。
如果程序员不够警惕——通常是因为他们太忙,这些机制就很容易被忽视。异常处理将错误处理直接置于编程语言中,有时甚至置于操作系统中。
异常是一种对象,它从出错地点被“抛出”,并被专门设计用来处理特定类型错误的相应的异常处理器“捕获”。
异常处理就像是与程序正常执行路径并行的,在错误发生时执行的另一条路径。
因为它是另一条完全分离的执行路径,所以它不会干扰正常的执行代码。
这往往使得代码编写变得简单,因为不需要被迫定期检查错误。
此外,被抛出的异常不像方法返回的错误值和方法设置的用来表示错误条件的标志位那样可以被忽略。
异常不能被忽略,所以它保证一定会在某处得到处理。
最后需要指出的是:异常提供了一种从错误状况进行可靠恢复的途径。
现在不再是只能退出程序,你可以经常进行校正,并恢复程序的执行,这些都有助于编写出更健壮的程序。Java的异常处理在众多的编程语言中格外引人注目,因为Java一开始就内置了异常处理,而且强制你必须使用它。
它是唯一可接受的错误报告方式。
如果没有编写正确的处理异常的代码,那么就会得到一条编译时的出错消息。
这种有保障的一致性有时会使得错误处理非常容易。值得注意的是,异常处理不是面向对象的特征——尽管在面向对象语言中异常常被表示成为一个对象。
异常处理在面向对象语言出现之前就已经存在了。1.12 并发编程
在计算机编程中有一个基本概念,就是在同一时刻处理多个任务的思想。
许多程序设计问题都要求,程序能够停下正在做的工作,转而处理某个其他问题,然后再返回主进程。
有许多方法可以实现这个目的。
最初,程序员们用所掌握的有关机器底层的知识来编写中断服务程序,主进程的挂起是通过硬件中断来触发的。
尽管这么做可以解决问题,但是其难度太大,而且不能移植,所以使得将程序移植到新型号的机器上时,既费时又费力。有时中断对于处理时间性强的任务是必需的,但是对于大量的其他问题于我们只是想把问题切分成多个可独立运行的部分(任务),从而提高程序的响应能力。
在程序中,这些彼此独立运行的部分称之为线程,上述概念被称为”并发”。
并发最常见的例子就是用户界面。通过使用任务,用户可以在揿[qìn]下按钮后快速得到一个响应,而不用被迫等待到程序完成当前任务为止。通常,线程只是一种为单一处理器分配执行时间的手段。
但是如果操作系统支持多处理器,那么每个任务都可以被指派给不同的处理器,并且它们是在真正地并行执行。
在语言级别上,多线程所带来的便利之一便是程序员不用再操心机器上是有多个处理器还是只有一个处理器。
由于程序在逻辑上被分为线程,所以如果机器拥有多个处理器,那么程序不需要特殊调整也能执行得更快。所有这些都使得并发看起来相当简单,但是有一个隐患:共享资源。
如果有多个并行任务都要访问同一项资源,那么就会出问题。
例如,两个进程不能同时向一台打印机发送信息。
为了解决这个问题,可以共享的资源,例如打印机,必须在使用期间被锁定。
因此,整个过程是:某个任务锁定某项资源令完成其任务,然后释放资源锁,使其他任务可以使用这项资源。Java的并发是内置于语言中的,Java SE5已经增添了大量额外的库支持。
-
怎样才能用稽核保存多个类中的不同对象对象
2015-07-30 17:44:42(使用:toArrry()方法) 登陆: 提示用户输入登陆的账号与密码,如果账号与密码这个用户已经存在集合中,那么登陆成功,否则登陆失败。 * * * * */ class User{//创建一个用户类 int name; String ... -
python beautifulsoup两个标签相同怎样分离_Python——BeautifulSoup4库的使用
2020-11-26 18:03:35使用requests库获取html页面并将其转换成字符串之后,需要进一步解析html页面格式,提取有用信息。BeautifulSoup4库,也被成为bs4库(后皆采用简写)用于解析和处理html和xml。1. 调用bs4库中最主要的便是bs类了,每...使用requests库获取html页面并将其转换成字符串之后,需要进一步解析html页面格式,提取有用信息。
BeautifulSoup4库,也被成为bs4库(后皆采用简写)用于解析和处理html和xml。1. 调用
bs4库中最主要的便是bs类了,每个实例化的对象都相当于一个html页面。
需要采用from-import导入bs类,同时通过BeautifulSoup()创建一个bs对象。
代码如下:import requestsfrom bs4 import BeautifulSoup r=requests.get("https://www.baidu.com/") r.encoding="utf-8"soup=BeautifulSoup(r.text)print(type(soup))
运行结果:2. 常用属性
创建的BeautifulSoup对象是一个树形结构,它包含html页面中的每一个Tag(标签)元素,可以通过<a>.<b>的形式获得。常见的属性如下:
head:
html页面<head>内容。
title:
html页面标题,在<head>之中,由<title>标记。
body:
html页面<body>内容。
p:
html页面第一个<p>内容。
strings:
html页面所有呈现在web上的字符串,即标签的内容。
stripped_string:
html页面所有呈现在web上的非空字符串。
接下来尝试爬取百度的标语“百度一下,你就知道”。
首先我们通过requests来建立请求,可以通过查看源代码找到对应部分。
如下所示:
所以直接通过调用<title>标签即可。
代码如下:import requestsfrom bs4 import BeautifulSoup r=requests.get("https://www.baidu.com/") r.encoding="utf-8"soup=BeautifulSoup(r.text) title=soup.titleprint(title)
结果如图所示:
3. 标签常用属性
每一个标签在bs4中也是一个对象,被称为tag对象,以糯米为例,常见结构如下:
<a class=“mnav” href="http://www.nuomi.com">糯米</a>
其中尖括号(<>)中标签的名字为 name,其他项为attrs,尖括号之间的内容为string所以常见的标签属性分为4种:
name:
字符串、标签的名字。
attrs:
字典、包含了原来页面tag的所有属性,比如href。
contrnts:
列表、tag下所有子tag的内容。
string:
字符串、tag所包含的文本,网页中的真实文字。
由于html可以在标签中嵌套其他的标签,所以string返回遵循以下原则:
① 如果标签内没有其他标签,string属性返回其中的内容;
② 如果标签内部还有标签,但只有一个标签,string返回最里面的标签内容;
③ 如果内部超过1层嵌套标签,则返回为none。
依然以百度为例,我们需要找到第一个<a>标签的string代码应当如下:
import requestsfrom bs4 import BeautifulSoup
r=requests.get("https://www.baidu.com/")
r.encoding="utf-8"soup=BeautifulSoup(r.text)print(soup.a.string)
结果如图所示:4. 调用find()与find_all()
html中,同一个特标签会有很多内容,比如百度首页<a>一共有13处,但值返回第一处。
所以这时候需要通过find与find_all来寻找,这两个方法都会遍历html文档按照条件返回内容。使用方法如下:BeautifulSoup.find_all(name,attrs,recursive,string,limit)
name: 以标签名字进行搜索,名字用字符串类型表示
attrs:按照标签的属性来搜索,需要列出属性的名字和值,用json方法表示
recursive:设置查找层次,只查找当前标签的西一层时使用recursiv=false
string:按照关键字查找string属性内容,采用string=开始
limit:返回结果个数,默认全部
至于find()使用方法如find_all()相同。BeautifulSoup.find_all(name,attrs,recursive,string)
区别在于find()只搜索第一个结果,find_all()返回所有结果。
我们尝试来通过find_all()来获取所有含有“百度”这一关键词的标签。
首先需要调用re库,re是python的标准库可以采用compile()对字符串的检索。
所以代码如下:
import requestsimport refrom bs4 import BeautifulSoup
r=requests.get("https://www.baidu.com/")
r.encoding="utf-8"soup=BeautifulSoup(r.text)
w=soup.find_all(string=re.compile("百度"))print(w)
结果如下: -
java 实例化对象 内存_实例化对象时对应的JVM内存结构
2021-02-27 14:23:31在语法级别上怎样完成对象创建呢?语法格式:类名 变量名 = new 类名();这样就完成了一个对象的创建。为了方便知识点的引入,设计一个学生类如下:publicclassStudent{int stu_no;//学号String stu_name;//姓名int ... -
如何销毁一个实例化对象_实例化对象时对应的JVM内存结构
2020-12-05 21:05:30在语法级别上怎样完成对象创建呢?语法格式:类名 变量名 = new 类名();这样就完成了一个对象的创建。为了方便知识点的引入,设计一个学生类如下:publicclassStudent{int stu_no;//学号 String stu_name;//姓名... -
实例化对象时对应的JVM内存结构
2020-07-15 23:28:20在语法级别上怎样完成对象创建呢? 语法格式: 类名 变量名 = new 类名(); 这样就完成了一个对象的创建。 为了方便知识点的引入,设计一个学生类如下: public class Student{ int stu_no;//学号 String stu_name;... -
创建实例之getinstance()函数
2011-03-23 10:05:00写在前面:一个类,只是表达对象是怎样的,而如果类实例化之后,它就是一个对象了。也就是说,实体化之后的东西,就是一个东西了。一个客观的实体,可以进 行各种的操作和设置了。而对象的实例化方法,也是比较多... -
利用单例模式创建新的Winform窗体
2021-02-06 15:48:57单例模式的核心是在应用程序的生命周期中只实例化一次当前类,让整个应用程序中只拥有一个当前类实例化的对象。...解决这样的问题我们最简单的想法就是如果每次我们单机按钮都使用相同的实例化对象, -
Java 重写 多态性_Java多态性,重写怎样实现多态性?
2021-02-28 08:33:32具体实现步骤:1、创建Figure类,在这个类当中,先要定义存储二维对象的尺寸,之后再定义有两个参数的构造方法,最后添加area()方法,这个方法计算对象的面积:publicclassFigure{doubledim1;doubledim2;Figure(dou.... -
[vue] 动态给vue的data添加一个新的属性时会发生什么?怎样解决?
2021-02-28 16:10:16如果在实例创建之后添加新的属性到实例上,它不会触发视图更新。如果想要使添加的值做到响应式,应当使用$set()来添加对象。 个人简介 我是歌谣,欢迎和大家一起交流前后端知识。放弃很容易, 但坚持一定很酷。欢迎... -
mysql索引 使用笔记_mysql笔记6_索引
2021-03-04 05:17:22什么是索引?数据库中的一个对象。在数据库中用来加速表的查询。通过使用快速路径访问方法定位数据,减少了磁盘的i/o。...怎样创建索引:create index 索引名 on 表名(字段名);使用索引:where之后加上索引字... -
java枚举类使用总结
2017-01-28 15:27:10创建实例数量有限并且固定的类,例如季节类,只有4个对象春,夏,秋,冬。这在java就叫作枚举类。 二.怎样定义枚举类 1.JDK1.5之前采用自定义枚举类,JDK1.5之后采用enum关键字 ... -
C#生成xml文件,怎样设置根节点的多个命名空间属性和schemalocation属性?
2015-08-06 13:41:04我使用了XmlDocument类,并定义了xmlnamespacemanager类,但是为什么我创建xmlnamespacemanager的对象并添加了两个命名空间之后,运行还是没有命名空间呢? ``` XmlNamespaceManager nsmgr = new ... -
jquery插件使用方法大全
2012-05-24 23:58:18jquery提供了很多便利的函数,如each(fn),但是使用这些函数的前提是:你使用的对象是Jquery对象。使一个Dom对象成为一个Jquery对象很简单,通过下面一些方式(只是一部分): 代码 var a = $("#cid"); var b = $... -
springjdbc:数据源
2019-10-05 12:44:11在项目中经常会需要连接数据库,可以使用的数据库有很多,平时我们还用连接池来管理数据库,现在学习了spring,我们知道了,spring的核心功能就是对象创建依赖注入 而我们在使用连接池时,连接对象也是通过... -
一些.net中应该知道的问题
2019-10-08 08:38:00每次当开发人员使用 new 运算符创建对象时,运行库都从托管堆为该对象分配内存。新创建的对象被放在上次创建的对象之后。垃圾回收器保存了一个指针,该指针总是指向托管堆中最后一个对象之后的内存空间。当新的对象... -
C#面试题——附答案(二)
2008-01-29 15:48:00每次当开发人员使用 new 运算符创建对象时,运行库都从托管堆为该对象分配内存。新创建的对象被放在上次创建的对象之后。垃圾回收器保存了一个指针,该指针总是指向托管堆中最后一 个对象之后的内存空间。当新的对象... -
C#面试题——附答案(二)
2006-12-04 20:45:53每次当开发人员使用 new 运算符创建对象时,运行库都从托管堆为该对象分配内存。新创建的对象被放在上次创建的对象之后。垃圾回收器保存了一个指针,该指针总是指向托管堆中最后一个对象之后的内存空间。当新的对象... -
设计模式总结(四)
2018-04-17 08:43:55【前言】 创建型设计模式和结构型设计模式之后,那么就来谈一下最后的一大类设计模式——行为型设计模式。【概念】 行为型模式涉及到算法和对象的分配,描述了对象和类的模式,以及他们之间的通信模式,行为型... -
理解COM套间(第二部分)
2014-02-21 10:36:29本文的前一部分阐述了为什么和怎样使用COM套间。读过之后,你会知道,调用CoInitialize或者...你还会知道,对象创建的时候也被放入到套间中,COM使用注册表中的ThreadingModel值决定将进程内对象放到什么类型